편의점에서 담배 재고를 쉽게 검수하기 위해 제작된 웹 서비스입니다. 매장 별로 '담배 목록'을 만들 수 있고 이를 편집함으로서 담배를 검수합니다. 담배 목록은 다른 사람들과 공유가 가능한데, 각 목록 생성자가 접근 권한을 관리하며, 소켓 통신을 활용해
어떻게 하면 예외를 간결하게 처리할 수 있을까? 예외를 아주 쉽고 재밌게 다룰 수 있게 되는 구조를 알려드리겠습니다.
처음엔 로그인 기능에 HTTP 세션을 사용했었는데, 세션정보가 앱서버에 저장되기 때문에, 서버가 Scale-out한다고 가정했을 때, 모든 서버가 세션 정보를 주고 받으며 동기화하기에는 비용이 크고, Sticky Session 방식으로 처리했을 경우엔 특정 서버에만 트
이 서비스는 동시 편집 기능을 지원합니다. 주된 플로우가 약 200개 항목의 담배 목록에서 각 담배의 재고를 입력하거나 순서를 재조정하는 것인데, 이 앱에서는 그러한 편집이 1-2초에 한 번씩 일어납니다. 동시 편집 기능을 구현하기 위해, HTTP 통신의 polling
요청 메시지를 처리하는 과정에서 발생하는 예외는 @ControllerAdvice의 @MessageExceptionHandler 가 글로벌하게 처리하게 했습니다. 기존 HTTP 예외를 처리하는 코드를 그대로 소켓용 @ControllerAdvice에 넣어줄 수도 있지만,
이 프로젝트의 소켓 통신에선, 클라이언트가 응답 메시지를 쉽게 처리 할 수 있도록 공통적인 필드(요청 사용자 id, 응답 해줄 채널)를 모든 소켓 요청마다 넣어주었습니다. 해당 필드가 추가된 서비스 DTO를 따로 만들지 않고, 기존 요청, 응답 DTO를 소켓 통신 용
세션 정보(토큰)와 담배 정보 등 자주 쓰이는 정보를 캐시했습니다. 캐시 저장소로 Redis와 Memcached 중에 고민했었는데, Memcached가 쓰기 성능은 좋지만, 캐시 특성상 읽기 기능이 훨씬 많이 쓰이기 때문에, 읽기 성능이 좋으면서 다양한 자료구조와 ev
요약 > Self-Invocation 문제 때문에 @Cacheable 대신 직접 캐시모듈을 만들어 사용했습니다. look-aside 전략으로 캐시와 DB를 조회하는 getCacheOrLoad 메서드를 예로 들자면 캐시타입(이름)과 캐시키, DB 조회 함수를 인자로 받습
레디스에 캐시를 저장할 때 도메인 엔티티 그대로 저장하니 연관관계 필드가 프록시로 저장되는 문제가 있어서, 레플렉션을 이용해 캐시모듈에서 DTO-엔티티 변환 메서드를 호출하도록 수정했습니다. 그래서 캐시를 저장할 땐 DTO로, 꺼내올 땐 엔티티로 꺼내오는 게 가능하도록
Redis에 @Transactional이 안 먹혀서 설정하는 법을 찾아보게 되었고, 그 과정에서 레디스 트랜잭션의 작동방식과 SessionCallback, LuaScript, 분산락 등 원자성을 보장하는 다양한 방법에 대해서도 알게 되었습니다.사용자가 사용하는 담배목록
여러개의 WAS와 캐시, DB가 복잡하게 얽힌 중복확인-삽입 로직을 상호 배타적으로 처리하기 위해 분산락을 적용했습니다. 성능이 좋고 적용이 간편한 Redisson 분산락을 사용했고, 재활용성이 높아보여 AOP로 만들었습니다. 분산락을 공부하며 분산락의 여러 종류와 장
Redis로 bulk insert할 방법을 찾아보다가 pipeline을 알게되었고, 메서드 수행시간을 기준으로 성능을 테스트 해봤는데 효과가 분명히 있어 CacheModule에 pipeline을 사용하는 bulk처리 메서드를 추가했습니다.직접 캐시 모듈 을 만들다보니,
이 앱으로 한 번 담배 검수를 시작하면 1-2초에 1번씩 업데이트 요청을 보내게 됩니다. 그 때마다 DB에 반영한다면 응답 시간도 길어지고, 많은 사람들이 요청을 보내는 등 대용량 트래픽이 발생했을 시 DB 병목이 유발될 것이라 생각했습니다. 그래서 업데이트 서비스가
요약 > Redis 서버 병목이 걱정되어 세션과 일반캐시 전용으로 서버를 분리해 2개의 레디스를 운용했습니다. Redis 설정클래스를 각각 만들었으며 RedisTemplate도 2개를 만들었습니다. 캐시모듈에선 캐시타입(이름)을 기반으로 적절한 RedisTemplate
nginx를 리버스프록시로 적용하면서 생기는 몇 문제를 해결했습니다.기존엔 클라이언트는 WAS와 직접 통신하고 커넥션을 형성했는데, 이젠 리버스프록시와 통신해야하니 리버스프록시에 CORS 관련 코드를 심어줘야했습니다.그리고 소켓 통신을 위한 커넥션을 형성하기 위해서 전
배포의 편의성과 서비스 무중단을 위해 Blue-Green 무중단 배포를 적용했습니다. 깃헙에 코드가 푸시되면 웹훅에 의해 젠킨스의 파이프라인이 실행됩니다. 이 파이프라인이 소스코드를 클론, 빌드하고 이미지로 만들어 DockerHub에 푸시하고, 앱서버에선 이미지를 Pu
목표하는 트래픽을 감당하기 위해 목표 TPS를 설정하고, jmeter로 소켓 메시지 부하를 생성해 pinpoint로 서버를 모니터링하거나 힙덤프를 둘러보면서 TPS나 CPU 이용률이 적은 이유, 메모리가 부족한 이유를 고민해보았습니다. TPS를 올리기 위해 각 서버들을
DB 병목을 완화하기 위해 레플리케이션을 적용했습니다. 각 DB별로 DataSource 빈을 등록하고, AbstractRoutingDatasource를 이용해 트랜잭션의 readOnly 여부에 따라 DataSource를 동적으로 선택하도록 했습니다. 그리고 LazyCo