분류 전체보기
-
Spring Boot + WebSocket으로 실시간 조회 인원 구현하기 (Part 2 - 구현 편)프로젝트, 트러블슈팅 2026. 3. 5. 21:02
Part 1에서 WebSocket, STOMP, 브로드캐스트 같은 개념을 정리했다.Part 2에서는 실제로 어떻게 구현했는지 코드를 하나씩 분석한다. 이 기능을 만들게 된 배경부터 시작해서 전체 동작 흐름을 다루겠다. 1. 이 기능을 왜 만들었나?이 프로젝트는 60대 이상 어르신을 위한 패션 쇼핑몰이다.상품 상세 페이지에 "현재 N명이 보고 있어요" 라는 문구를 실시간으로 표시하는 기능을 만들었다. 단순해 보이지만 이 기능에는 두 가지 핵심 요구사항이 있다.사용자가 페이지에 들어올 때 즉시 숫자가 올라가야 한다사용자가 페이지를 떠날 때 즉시 숫자가 내려가야 한다새로고침 없이 다른 사람의 화면도 자동으로 업데이트돼야 하기 때문에 WebSocket + STOMP + Redis 조합으로 구현했다.2. 프로젝..
-
Spring Boot + WebSocket으로 실시간 조회 인원 구현하기 (Part 1 - 개념 편)프로젝트, 트러블슈팅 2026. 3. 5. 11:12
실시간 기능을 구현할 때 가장 먼저 떠오르는 게 WebSocket이다.그런데 막상 Spring Boot에서 WebSocket을 쓰려고 찾아보면 STOMP, SimpMessagingTemplate, MessageBroker 같은 낯선 개념들이 쏟아진다. 단순히 "실시간으로 숫자 하나 보여주면 되는 거 아닌가?"라고 생각했는데 생각보다 알아야 할 게 많다.이 글은 WebSocket을 처음 접하는 사람도 이해할 수 있도록 개념부터 차근차근 정리한다. 코드 분석과 실제 동작 흐름은 Part 2에서 다룬다.1. HTTP의 한계 - 왜 WebSocket이 필요한가?일반적인 HTTP 통신은 이렇게 동작한다.[클라이언트] [서버] | ..
-
Spring Boot + OpenAI API로 의류 소재 설명 자동 생성하기프로젝트, 트러블슈팅 2026. 3. 5. 10:04
시작하면서온길 프로젝트는 60대 이상 어르신을 위한 패션 쇼핑몰이다. 상품 페이지에는 "면 60%, 폴리에스터 40%" 같은 소재 정보가 표시되는데, 어르신들 입장에서는 이게 무슨 의미인지 바로 알기 어렵다.그래서 이 소재 정보를 AI에게 넘겨서 이런 식으로 자동 변환해주는 기능을 만들었다.면 60%, 폴리에스터 40% ↓ AI 변환장점: 땀 흡수가 잘 돼요 / 피부에 자극 없어요단점: 구겨지기 쉬워요 / 오래 입으면 늘어나요세탁법: 찬물에 손세탁해요 / 그늘에서 말려요LLM이란?LLM(Large Language Model)은 대규모 언어 모델이다. 엄청난 양의 텍스트를 학습해서 문맥을 이해하고, 자연스러운 글을 생성할 수 있는 AI다. ChatGPT가 대표적인 예시다.LLM을 서비스에 연..
-
실시간 할인 알림 구현기 — Spring SSE + Scheduler프로젝트, 트러블슈팅 2026. 3. 4. 19:05
시작하면서온길 프로젝트에서 "원하는 가격이 되면 알림받기" 기능을 구현했다. 사용자가 관심 상품의 할인율을 설정해두면, 실제 가격이 목표가 이하로 떨어졌을 때 실시간으로 알림을 받는 기능이다.구현하면서 가장 고민했던 부분은 두 가지였다. 하나는 주기적으로 가격을 체크하는 스케줄러, 다른 하나는 조건을 만족했을 때 실시간으로 클라이언트에 알림을 전달하는 방식이었다. 이 글은 그 과정을 정리한 것이다.전체 흐름은 이렇다.[사용자] 할인율 선택 및 알림 등록 ↓[DB] PriceAlert 저장 ↓[스케줄러] 30초마다 가격 체크 ↓ (목표가 이하 도달 시)[DB] Notification 저장 + [SSE] 실시간 푸시 ↓[클라이언트] 알림 수신도메인 설계엔티티를 두 개로 분리했다.Pric..
-
도움돼요 토글, 비관적 락 대신 DB 원자적 UPDATE를 선택한 이유프로젝트, 트러블슈팅 2026. 3. 4. 15:42
시작하면서리뷰에 "도움돼요" 버튼을 구현하면서 동시성 문제를 처음 마주쳤다. 여러 사람이 동시에 같은 리뷰에 도움돼요를 누르면 DataIntegrityViolationException이 발생하거나 helpfulCount가 실제 레코드 수와 달라지는 문제였다. 처음엔 교과서적인 해결책인 비관적 락을 적용했지만, 막상 써보니 도움돼요 같은 기능에는 과하다는 생각이 들었다. 이 글은 왜 비관적 락을 제거하고 DB 원자적 UPDATE로 바꿨는지, 그 과정을 정리한 것이다.동시성 문제란?처음 구현한 코드는 이렇게 생겼다.@Transactionalpublic ReviewHelpfulResponse toggleHelpful(Long reviewId, Long userId) { Review review = rev..
-
Redis 캐시에 빈 배열이 영구 저장되는 버그 트러블슈팅프로젝트, 트러블슈팅 2026. 3. 4. 15:08
시작하면서프로젝트에서 카테고리와 브랜드 조회 API에 Redis 캐싱을 붙였다. 자주 바뀌지 않는 데이터라 캐싱하면 성능이 좋아질 거라 생각했다. 근데 배포하고 나서 API를 호출하면 데이터가 있는데도 빈 배열이 계속 반환됐다. 원인을 찾아보니 Redis가 빈 배열을 영구 저장하고 있었다.Redis란?Redis는 메모리 기반 Key-Value 저장소다. 데이터를 디스크가 아닌 메모리에 저장하기 때문에 조회 속도가 극도로 빠르다.MySQL → 디스크 저장 → 조회 50~100msRedis → 메모리 저장 → 조회 1~5ms캐싱에 많이 쓰이는 이유가 여기 있다. 자주 조회되는 데이터를 Redis에 저장해두면 DB까지 가지 않아도 되니까 응답 속도가 훨씬 빨라진다.Redis 캐싱의 기본 흐름은 이렇다.첫 번째..
-
연합동아리 코테이토 12기 회고록회고 2026. 3. 4. 00:05
이번에 IT 연합동이리 코테이토(Cotato)에서의 백엔드 파트로 6개월 간의 활동이 끝나 회고겸해서 여러 느낀점을 써보려고한다. 사실 끝난지는 1~2주 정도 지났지만 저번주에 있던 정처기 시험 이슈로 인해 이제야 쓰게됐다... 늦은 만큼 열심히 써보겠다!!코테이토 느낀점1. 다양한 스터디 문화 이 부분은 코테이토의 가장 큰 장점이라고 생각한다. 처음 들어갔을 때 오티 바로 다음 주에 스터디를 개설 + 신청을 할 수 있는 시간을 주어졌다. 이때 나는 우리 회장님이 만드신 취뽀취뽀 스터디를 하고 싶어 11시에 대기타서 바로 신청하였다(참고로 스터디 신청은 선착순입니다...!!!). 먼저 스터디에 대해 간단히 설명하면 매주 일요일 대면으로 만나 코테 3문제 + '이것이 취업을 위한 컴퓨터 과학이다 wit..
-
복합 유니크 제약조건으로 중복 데이터 막기프로젝트, 트러블슈팅 2026. 3. 1. 23:27
시작하면서프로젝트에서 리뷰 도움돼요 기능을 만들면서 같은 사람이 같은 리뷰에 도움돼요를 중복으로 누르는 걸 막아야 했다. 처음엔 서비스 로직에서 중복 체크를 하려 했는데, DB 레벨에서 아예 막는 게 더 확실하다는 걸 알게 됐다. 복합 유니크 제약조건을 쓰면 된다.유니크 제약조건특정 컬럼의 값이 테이블에서 딱 한 번만 존재해야 한다는 규칙이다. DB가 직접 막아주기 때문에 서비스 로직에서 중복 체크를 하지 않아도 된다.가장 흔한 예시가 이메일이다.@Column(unique = true)private String email;이렇게 하면 같은 이메일로 두 번 가입하려 할 때 DB에서 바로 에러를 던진다. 이게 단일 유니크 제약조건이다.복합 유니크가 필요한 이유도움돼요 기능에서 단일 유니크를 쓰면 문제가 생긴..