테크 뉴스
테크 뉴스 더 보기
기술 블로그
하이버네이트의 시간은 거꾸로 간다
하이버네이트의 시간은 거꾸로 간다스프링부트 버전을 업그레이드하는 과정에서 발견된 버그 해결기• 문제 좁히기• 1. 배포 전후로 나노초 값이 다른 이유• 의심 1: 자바 버전 업그레이드의 영향 (11 ➡️ 17)• 의심 2: 하이버네이트 버전 업그레이드의 영향 (5.6.5 ➡️ 6.5.5)• 2. 조회 과정에서 음수의 나노초가 발생하는 이유저희 딜리버리 프로덕트 개발팀은 AWS MSK 버전 업그레이드에 대응하기 위해 관리 중인 시스템들의 스프링부트 버전을 3.x대로 업데이트했습니다. 이미 다른 프로젝트에서 스프링부트 버전업을 경험한 데다, 팀원들이 겪었던 이슈들을 잘 정리한 문서 덕분에 큰 어려움 없이 작업을 진행할 수 있었습니다. 업데이트된 프로젝트는 QA를 무사히 통과해 운영 환경에 배포되었고, 배포 후에도 별다른 문제 없이 잘 동작하는 듯했습니다.하지만 다음 날 자정 직전에 에러 알림이 발생하기 시작했습니다. 해당 버그로 인해 일부 배송 매니저는 배송 업무를 볼 때, 사용하는 컬리버드 앱에 로그인 자체가 불가능한 상태였습니다. 특히 에러가 발생한 시점이 샛별 배송이 활발하게 진행되는 시간대였기 때문에, 배송 업무에 차질이 없도록 신속하게 롤백을 진행한 후 다음날 에러 원인을 파악하기 시작했습니다.먼저 문제의 원인을 좁히기위해서 에러 로그를 확인했습니다.이 로그를 통해, DB에서 데이터를 조회하여 객체로 변환하는 과정에서 DateTime 형식에 문제가 있음을 파악할 수 있었습니다. 음수의 나노초 값을 가지는 데이터가 원인인 것 같아서, DB에 그런 데이터가 있는지 확인해보았습니다.하지만 확인 결과, 음수의 나노초를 포함한 날짜나 시각 데이터는 존재하지 않았고, 모든 데이터가 0 또는 양수 값을 가지고 있었습니다.데이터를 검토하면서 배포 이전과 이후의 시점에서 나노초 값이 미묘하게 달라졌다는 점을 발견했습니다. 배포 이전의 데이터에는 나노초가 없었으나, 배포 이후에는 나노초가 포함된 데이터가 생긴 것이었습니다.이로 인해 두 가지 의문이 생겼습니다• 배포 전후로 나노초 값이 다른 이유는 무엇일까?• DB에는 정상 범위의 나노초가 저장되어 있는데, 왜 조회 과정에서 음수의 나노초가 발생하는 걸까?1. 배포 전후로 나노초 값이 다른 이유의심 1: 자바 버전 업그레이드의 영향 (11 ➡️ 17)배포 전후의 차이점을 분석하던 중, 먼저 자바 버전의 변경을 의심했습니다. 스프링 부트 3.x는 최소 JDK 17을 요구하므로, 자바 버전을 11에서 17로 업그레이드했고, 이로 인한 영향을 고려해 LocalTime 로직의 변화를 확인해봤습니다. 하지만 자바 11과 17의 LocalTime 로직에 수정된 부분은 없었습니다.또한, 로컬 환경에서 테스트한 결과, 배포 이전에도 나노초 값은 0이 아닌 상태였으며, 저장 시점에 나노초 값을 버리고 DB에 저장된다는 사실을 알게 되었습니다.의심 2: 하이버네이트 버전 업그레이드의 영향 (5.6.5 ➡️ 6.5.5)자바 버전이 원인이 아닌 것으로 확인된 후, DB 저장을 처리하는 하이버네이트 로직을 의심했습니다. 실
마켓컬리
·
오늘
CallKit에서 AVAudioSession 사용하기
이 글은 mVoIP 서비스를 준비하면서 CallKit을 통해 오디오를 사용하는 개발자를 대상으로 작성되었습니다.iOS에서 VoIP를 구현하기 위해서는 PushKit과 CallKit에 대한 배경지식이 필요합니다.특히 CallKit은 CXCallController와 CXProvider의 역할에 대한 이해가 중요하며, 이와 관련된 자세한 내용은 Apple 공식 문서를 참고하면 많은 도움이 될 것입니다.그러나 CallKit에서 사용되는 오디오에 대해서는 자세히 설명된 자료를 찾기가 쉽지 않습니다. 이 글에서는 CallKit에서 사용되는 오디오 내용을 중점적으로 다루고자 합니다.iOS에서 AVAudioSession은 앱의 오디오 사용 시 하드웨어를 제어하는 중개자 역할을 합니다.일반적으로 AVAudioSession은 주로 영상이나 음악 재생, 또는 마이크 녹음 시 오디오 활성화에 사용됩니다.하지만 CallKit과 mVoIP 환경에서의 AVAudioSession 사용은 단순 음원 재생이나 녹음과 다른 환경 설정과 활성화 방식이 필요합니다.• None 예를 들어, mVoIP에는 playAndRecord 카테고리를 사용해야 하며, 모드는 voiceChat이나 videoChat을 사용해야 합니다.• None 오디오의 활성화/비활성화는 CallKit에 의해 관리됩니다.오디오 환경 설정과 활성화 방식을 제대로 이해하지 못한 채 사용하다 보면 예상치 못한 이슈가 발생할 수 있습니다.전화의 특성상 대부분 중요한 문제(무음이나 통화 품질 저하)였고, 이를 해결하기 위해 많은 시행착오를 겪었습니다.CallKit에서 발생하는 오디오 이슈 현상• None 수신자나 발신자의 소리가 들리지 않음• None 기본 수화기와 스피커가 강제 전환되는 현상• None 통화 종료 후 다음 통화가 비정상적으로 동작하는 현상다른 앱에서 오디오 사용 중 (음원 재생이나 녹음 중)• None 전화 수신 시 입출력 포트 경로가 강제로 변경되는 현상• None 전화 종료/거절 시 이전에 재생되던 음원이 이어서 재생되지 않는 현상다양한 환경(블루투스, 카플레이, 이어셋 등)에서의 호환성• None 카플레이(CarPlay) 환경에서 통화는 진행되지만, 마이크가 동작하지 않는 현상• None 블루투스 인포메이션 화면에 통화 종료 이후에도 통화 중 화면이 노출되는 현상• None 에어팟으로 통화 중 연결이 끊기고 스피커나 기본 수화기로 통화가 전환되는 현상애플에서 제공하는 샘플 코드를 참고하여 이 이슈를 해결하기 위해 필요한 핵심 개념들을 설명하겠습니다.애플에서 제공한 SpeakerBox 샘플 코드를 바탕으로 설명하겠습니다.이 샘플은 CallKit, AVAudioSession, 그리고 AVAudioEngine을 활용하여 마이크로 입력된 음성을 아이폰의 내장 수화기로 출력하는 간단한 예제입니다.이 설명에서는 오디오 환경 설정 방법과 각 인터페이스 호출 시 주의해야 할 점을 중점적으로 다루겠습니다. 특히 다음과 같은 중요한 사항을 살펴보겠습니다:이를 통해 mVoIP 앱 개발 시 발생할 수 있는 일반적인
SK텔레콤
·
오늘
[SpringBatch 연재 04] FlatFileItemReader로 단순 파일 읽고, FlatFileItemWriter로 파일에 쓰기
• None FlatFileItemReader는 Spring Batch에서 제공하는 기본적인 ItemReader로, 텍스트 파일로부터 데이터를 읽습니다.• None 고정 길이, 구분자 기반, 멀티라인 등 다양한 형식의 텍스트 파일을 지원하며, 다음과 같은 장점을 가집니다.• None 간단하고 효율적인 구현: 설정 및 사용이 간편하며, 대규모 데이터 처리에도 효율적입니다.• None 다양한 텍스트 파일 형식 지원: 고정 길이, 구분자 기반, 멀티라인 등 다양한 형식의 텍스트 파일을 읽을 수 있습니다.• None 확장 가능성: 토크나이저, 필터 등을 통해 기능을 확장할 수 있습니다.• None 사용처: 고정 길이, 구분자 기반, 멀티라인 등 다양한 형식의 텍스트 파일 데이터 처리• None 장점: 간단하고 효율적인 구현, 다양한 텍스트 파일 형식 지원• None 단점: 복잡한 데이터 구조 처리에는 적합하지 않음• None Resource: 읽을 텍스트 파일을 지정합니다.• None LineMapper: 텍스트 파일의 각 라인을 Item으로 변환하는 역할을 합니다.• None LineTokenizer: 텍스트 파일의 각 라인을 토큰으로 분리하는 역할을 합니다.• None FieldSetMapper: 토큰을 Item의 속성에 매핑하는 역할을 합니다.• None SkippableLineMapper: 오류 발생 시 해당 라인을 건너뛸 수 있도록 합니다.• None LineCallbackHandler: 라인별로 처리를 수행할 수 있도록 합니다.• None ReadListener: 읽기 시작, 종료, 오류 발생 등의 이벤트를 처리할 수 있도록 합니다.• None 읽어들인 정보를 Customer 객체에 매핑할 수 있도록 객체를 정의한다.• None 다음과 같이 FlatFileItemReader를 생성하고, Customer 객체에 등록하여 반환한다.• None• None 클래스 패스 내부에 존재하는 csv 파일을 읽어들인다.• None• None 파일 데이터의 인코딩을 추가한다.• None• None 구분자로 설정되어 있음을 의미한다.• None• None 구분자를 무엇으로할지 지정한다.• None• None 구분자로 구분된 데이터의 이름을 지정한다.• None• None 구분된 데이터를 어느 모델에 넣을지 클래스 타입을 지정한다.• None 이제 CSV 플랫파일을 이용하여 단순히 다시 새로운 플랫파일로 저장하고 탭으로 구분된 배치를 작성해보자.• None 지금까지 단순한 FlatFile을 읽는다 이는 csv 형식의 파일이며, 이 파일을 읽어 탭으로 구분된 파일을 새로 작성하는 배치를 작성했다.• None FlatFileItemReader로 파일을 읽었다. 이때 파일의 특성에 따라 FlatFileItemReaderBuilder를 이용하여 쉽게 구성했다.• None FlatFileItemWriter는 Spring Batch에서 제공하는 ItemWriter 인터페이스를 구현하는 클래스이다.• None 데이터를 텍스트 파일로 출력하는 데 사용된다.• None
SK텔레콤
·
오늘
토스 피플 #2: UX 라이팅의 새로운 기준
오늘은 토스에서 UX Writing Team Leader로 근무하고 있는 김자유님의 인터뷰를 공유드려요. 자유님은 푸시 알림을 포함해 토스 앱 안에 보이는 모든 메시지의 보이스톤을 일관되게 통일하는 업무를 하고 있어요.토스의 첫 UX Writer로 입사하신 자유님은 라이팅 원칙을 세우고 전사적으로 읽기 쉽고 편한 UX Writing을 전파한 장본인인데요. 단순히 문구를 개선하는 것뿐만 아니라, 좋은 문구 작성을 자동화하는 시스템을 만들기도 하셨죠. 자유님은 어떻게 UX Writer라는 직업을 선택하게 됐을까요? 오늘은 자유님의 커리어 이야기를 나눠드려요.토스에서 했던 프로젝트 중 가장 기억에 남는 작업은 무엇인가요?디자인 툴에서 토스의 보이스톤을 자동으로 적용해주는 잡초 제거기와, 반복적으로 사용하는 에러 메시지 템플릿을 컴포넌트처럼 쓸 수 있게 만든 프레이머링 등 여러 프로젝트들이 떠오르는데요. 많이들 기억하시는 건 아무래도 토스의 8가지 라이팅 원칙들 아닐까 싶어요. 그중에서도 특히 불필요한 단어를 가리키는 말로 ‘잡초’라는 라이팅 용어를 만든 게 임팩트가 컸던 것 같아요. 토스 내부에서 소통하기 위해 만든 것이었는데, 토스 밖에서 다른 분들이 사용하시는 것도 종종 봤거든요.가장 최근에 했던 작업으로는 디자인 해킹이 기억에 남네요. 제가 입사했을 때와 비교해서, 이제는 라이팅 원칙이 자리 잡고, 그 톤을 적용할 수 있는 장치도 여럿 마련되어서 토스만의 보이스톤이 많이 정립되었어요. 그런데 문장 하나만 보면 괜찮은데 전체 화면을 읽어봤을 때 흐름이 끊긴다든지, 앞에서 했던 말을 뒤에서 반복한다든지 하는 문제가 보이기 시작했어요. 이건 문장을 개선하기보다 내러티브 자체를 처음부터 잘 만들어야 하는 거라, 시스템보다는 교육을 통해 해결해야겠다고 생각했죠.그래서 토스를 오래 다닌 디자이너분들의 뇌를 해킹하는 세션을 만들었어요. 개선할 필요가 있는 화면을 드리고, 어떻게 개선할 것 같은지 만들어보도록 부탁드렸죠. 그리고 똑같은 화면을 신규 입사자분들에게도 드려봤어요. 똑같은 화면을 보고 저 디자이너와 나는 어떻게 다르게 개선할까? 이 내용에 집중해서 교육 자료를 만들었었어요. 실제로 교육을 들은 분들의 화면 구성력이 달라지는 걸 눈으로 확인했을 때, 엄청 신기하고 뿌듯했죠. (자세한 내용은 곧 블로그에 공유될 예정)그래프를 보니, 초반에 엄청 자잘한 점들이 많이 찍혀있네요. 어떤 의미일까요?막 학교 졸업 전후 시기인데요. 저는 오히려 학교에 다닐 때는 목표가 뚜렷했다가 졸업할 즈음 방황을 했어요. 저는 중학교 때부터 해외 시장을 대상으로 일하고 싶어 했거든요. 그래서 학교에서 영문학을 전공하고, 새내기 때도 잘 안 놀고 HSK 자격증, 부전공, 무역 관련 대외 활동 등등 스펙 쌓느라 바빴어요. 동기들이 졸업하고 뭐 할지 고민이라고 했을 때, 크게 공감을 못 했어요.그래서 졸업 전에 해외 영업직으로 잠깐 인턴을 했어요. 그런데 웬걸, 하루하루가 너무 지루한 거예요. 그때부터 너무 당황스러웠죠. 어렸을 때부터 꿈 꿔왔던 직업인데… 기대한 것과 너
비바리퍼블리카
·
하루 전
ELK 환경에서 좀 더 정교한 이슈 트래킹 Part1 - 이슈 트래킹 기반 마련하기
bread.young ELK 환경에서 로그를 어떻게 쌓고, 보여주는지 쉽게 설명된 글입니다. 이슈 트래킹을 쉽게 하는 방법은 개발자라면 누구나 고민하는 부분인데 어떻게 풀어내었는지 궁금하네요🧐!!rain.drop ELK 환경에서의 로깅 방식과 Sentry를 이용한 이슈 트래킹 전략을 쉽게 풀어쓴 글입니다. ELK 환경을 구축하려는 분, 혹은 동일한 문제에 고민을 갖고 계신 분들께 추천드립니다~!안녕하세요. 해외결제서비스유닛에서 서버 개발 업무를 맡고 있는 포도입니다.지난 포스팅인 Kotlin으로 Spring AOP 극복하기!에 이어 이번 글에서는, Spring 기반의 서비스를 운영하는 개발자를 대상으로 이슈 트래킹 전략을 다룹니다. 복잡한 비즈니스 로직과 트랜잭션을 처리하는 환경에서 효과적인 이슈 트래킹을 고민하는 개발자에게 유용할 것입니다.이번에 다루게 될 내용은, 이슈 트래킹 전략을 단계적으로 발전시키는 과정을 3개의 Part로 나누어 설명하려고 합니다. 또한, 앞으로 소개될 내용의 전반적인 코드는 hello-elk-thread에서 확인하실 수 있습니다.• 먼저 이번 포스팅인 Part1에서는, ELK 환경에서의 Request 요청 로깅과 Sentry를 사용한 이슈 트래킹 전략의 기반을 마련합니다. 그리고 몇 가지 문제점을 설명하려고 합니다.• Part2에서는, Part1에서 설명한 문제점을 해결하기 위해 ThreadContext를 활용하여 이슈 트래킹 전략을 발전시킨 과정을 설명하려고 합니다.• 마지막인 Part3에서는, Part2에서 다루지 못하는 배치성 API, 비동기 상황에서의 극복을 위해 Multi Thread Context를 활용하여 발전시킨 과정을 설명하려고 합니다.만약에 ELK, Sentry를 사용한 이슈 트래킹 전략을 이미 사용하고 있다면, Part2부터 읽기를 시작하는 것을 추천드립니다.ELK 환경에서 로그 기반 마련하기이슈 발생 시 원인을 분석하기 위해서는 로그 정보를 확인하는 과정은 필수적일 것입니다. 하지만 로그를 분석하기 위한 별도의 환경이 구성되어 있지 않다면, 파일로 쌓아둔 로그 파일들을 하나하나 찾아가며 원인 분석을 해야 할 것입니다.ELK 스택은 애플리케이션에서 출력한 로그를 ElasticSearch에 저장하고, Kibana 대시보드를 통해서 로그 데이터의 가시성을 확보할 수 있는 환경을 제공합니다. 예를 들어 본 포스팅에서 설명할 내용처럼 Request 요청에 대한 로그 정보를 Kibana 대시보드를 통해 정보를 획득하는 것입니다.따라서 이슈 발생 시 쌓아둔 로그 파일에 접근하는 것이 아닌, Kibana 대시보드에 접근하여 로그 데이터를 빠르게 획득하여 보다 기민한 원인 분석을 할 수 있을 것입니다. 만약에 로그 분석을 하기 위한 별도의 환경이 구성되어 있지 않다면, ELK 스택을 활용한 본 포스팅의 내용이 도움이 될 것입니다.RequestLoggingFilter를 사용한 Request 요청 로깅Java Servlet의 기술 중 Servlet Filter는 클라이언트의 요청과 서버의 응답 사이에 위치하
카카오페이
·
2일 전
MySQL Multi-Source Replication - MSA로 서비스를 쪼개면 나중에는 반대로 DB를 합쳐야한다...?
코로나가 발생하기 전인 2010년대 중반쯤부터 MSA(Microservices Architecture)가 엄청 유행했죠.특히 Netflix, Amazon과 같은 대형 IT기업에서 MSA를 채택하면서 주목받고모놀리식 아키텍처(Monolithic Architecture, MA)로는 더이상 관리하기 어려워진 회사들에서 빠르게 도입하기 시작했습니다.게다가 당시에 Docker & Kubernetes(k8s)같은 컨테이너 기반의 플랫폼이 나오면서 MSA 도입하기도 상대적으로 쉬워졌고꼭 AWS, Azure, GCP와 같은 클라우드가 아니라도 On-Premise에서도 클라우드처럼 인프라 제공이 가능해지면서 많이 보편화되었습니다.다만 지금도 그렇지만 MSA라는게 정석적인 방법이 없다보니누구는 "k8s를 써야 MSA야!"라고 하고 누구는 "서브도메인으로만 나눠도 MSA야!"라고 하는 등 다양한 방법론이 많이 나왔습니다.저도 그쯤해서 약팔기 위해 "GDG DevFest Seoul 2019" 행사에서 "웹서비스에서 MSA 톺아보기"라는 주제로 발표도 했었던 기억이 있네요.MSA의 가장 큰 특징은 바로 이거죠• None 마이크로 서비스의 독립성특히 서비스의 독립성으로 인해서 언어/프레임워크가 달라도 되고 DB도 다양한걸 도입할 수 있죠.하지만 서비스의 독립성이 오히려 가장 큰 단점이 되기도 하는데서비스/DB간에 독립적이다보니 데이터의 일관성을 지키기가 사실 불가능합니다.물론 서비스만 독립적이고 다 같이 1개의 DB를 바라보면 되긴하겠지만... 그럼 트래픽이나 부하 분산이 안되겠죠? ㅎㅎ;;그래서 일반적으론 MSA에서는 아래 그림처럼 서비스(기능, 도메인) 단위로 DB를 쪼갭니다.보면 계정(account), 장바구니(inventory), shipping(상품) 기준으로 서비스와 DB가 나눠져있습니다.그럼 HA고려해서 웹/API서버, DB 모두 이중화하고 auto scaling이 적용되어있다면정상적인 상황 내에선 안정적으로 동작하고 비용이나 운영적인 부분에 대해서 효율적으로 관리할 수 있을겁니다.근데 언제나 그렇듯 늘 새로운 요건이 생기죠.대충 온라인 쇼핑몰 같으니 가장 많이 시도해보는 기능 중에 하나인"데이터 기반 개인화 상품 추천" 기능을 만들어보자는 요건이 생겼다고 해봅시다.개인(=account)화 상품(=shipping)을 "추천"해야되는데 그럼 구매했던 내역이나 장바구니 목록(=inventory)의 데이터가 필요하겠죠?그럼 문제는 3개의 DB에서 JOIN을 해서 "유저별 구매/관심 상품" 테이블을 만들어줘야하는데물리적으로 분리된 DB에서 JOIN연산은 사실상 불가능하죠.그렇다고 전체 유저/상품/장바구니 데이터를 뽑아서 pandas dataframe으로 곱연산을 직접한다면...?당연히 전체는 커녕 유저 1명의 데이터도 만들어내기 쉽지 않을겁니다.실제로 운영하는 서비스에서는 테이블/데이터 구조가 그렇게 간단하지 않을테니깐요!그리고 만약에 실시간으로 추천해야된다면...? 어휴 상상만해도 끔찍합니다...이렇다보니 막상 MSA를 도입해서 서비스와 DB를 쪼개놨더니
SK텔레콤
·
2일 전
기술 블로그 더 보기
디스커버리
디스커버리 콘텐츠 더 보기
테크 뉴스
테크 뉴스 더 보기
코드너리에서 이용할 수 있는
새로운 기능
새로운 기능
지금 확인해 보세요!
이달의 컨퍼런스
컨퍼런스 일정 더 보기