분류 전체보기 87

Spring 예외 처리 전략: @ControllerAdvice와 @ExceptionHandler 활용법

Spring으로 API를 개발하다 보면 예외 처리 코드가 점점 늘어납니다. 사용자가 없으면 UserNotFoundException, 권한이 없으면 AccessDeniedException, 입력값이 잘못되면 IllegalArgumentException... 이런 예외들을 컨트롤러마다 try-catch로 잡다 보면 코드가 지저분해지는 경험, 한 번쯤 있으실 겁니다. 오늘은 이 문제를 깔끔하게 해결하는 방법을 알아보겠습니다.문제 상황: try-catch가 여기저기 널려있다먼저 흔히 볼 수 있는 코드를 보겠습니다.@RestController@RequestMapping("/api/users")public class UserController { @GetMapping("/{id}") public Resp..

@RestController vs @Controller: API 서버와 화면 서버의 차이

Spring Boot를 배우다 보면 @Controller와 @RestController 두 가지 어노테이션을 만나게 됩니다. 둘 다 컨트롤러인데 뭐가 다른 걸까요? 그냥 @RestController만 쓰면 되는 거 아닌가요?처음엔 저도 그렇게 생각했습니다. 하지만 이 둘의 차이를 이해하면, 서버가 클라이언트에게 무엇을 돌려주는지에 대한 근본적인 개념이 잡히게 됩니다. 오늘은 최대한 쉽게 이 차이를 설명해보겠습니다.먼저, 서버가 할 수 있는 두 가지 일웹 서버는 크게 두 가지 종류의 응답을 할 수 있습니다:데이터만 주는 서버 (API 서버)화면(HTML)을 주는 서버 (웹 애플리케이션 서버)음식점으로 비유하면 이렇습니다:API 서버: 재료만 파는 곳입니다. "소고기 200g 주세요" 하면 소고기만 딱 줍니..

JPA N+1 문제: 개발자를 가장 괴롭히는 성능 이슈와 해결책

JPA를 처음 배우고 신나게 프로젝트를 진행하다 보면, 어느 순간 이상한 현상을 마주하게 됩니다. 분명히 데이터를 한 번만 조회했는데, 콘솔에 SQL 쿼리가 수십 개씩 찍혀있는 거죠. "어? 나는 쿼리 하나만 날렸는데...?" 하고 당황하신 경험, 한 번쯤 있으실 겁니다.이것이 바로 그 유명한 N+1 문제입니다. JPA를 사용하는 개발자라면 반드시 이해하고 넘어가야 할 핵심 주제이기도 하죠. 오늘은 이 N+1 문제가 정확히 무엇인지, 왜 발생하는지, 그리고 어떻게 해결할 수 있는지 차근차근 알아보겠습니다.N+1 문제란?이름의 의미부터 이해하기N+1이라는 이름은 발생하는 쿼리의 개수에서 유래했습니다.1: 처음에 데이터 목록을 가져오는 쿼리 1개N: 목록의 각 항목(N개)마다 연관된 데이터를 가져오는 쿼리 ..

JPA 영속성 컨텍스트(Persistence Context): 1차 캐시와 쓰기 지연이 주는 이점

JPA를 처음 배울 때 가장 헷갈리는 개념 중 하나가 바로 영속성 컨텍스트(Persistence Context)입니다. "영속성"이라는 단어부터가 일상에서 잘 쓰지 않는 표현이다 보니, 저도 처음엔 이게 대체 뭔가 싶었습니다. 그런데 알고 보면 영속성 컨텍스트는 꽤 단순한 아이디어입니다. 그리고 이걸 이해하면 JPA가 왜 그렇게 동작하는지, em.persist()를 호출해도 왜 바로 INSERT 쿼리가 나가지 않는지 자연스럽게 이해할 수 있습니다. 이번 글에서는 영속성 컨텍스트가 무엇인지, 그리고 1차 캐시와 쓰기 지연이 어떤 이점을 주는지 초보자 눈높이에서 설명해보겠습니다.영속성 컨텍스트란?쉽게 말하면 "임시 저장소" 영속성 컨텍스트를 한마디로 표현하면 "엔티티를 담아두는 임시 저장소"입니다.마트에서 ..

@Transactional의 동작 원리: 트랜잭션은 어떻게 시작되고 롤백될까?

Spring을 사용하면서 @Transactional만큼 자주 쓰이면서도, 정확한 동작 원리는 모르고 쓰는 어노테이션도 드물 것 같습니다. "메소드에 붙이면 트랜잭션이 알아서 된다"는 건 알겠는데, 도대체 어떻게 알아서 되는 걸까요?이번 글에서는 @Transactional이 AOP를 통해 어떻게 동작하는지, 트랜잭션이 언제 시작되고 언제 롤백되는지 초보자도 이해할 수 있도록 풀어보겠습니다.트랜잭션이란?본격적인 설명에 앞서, 트랜잭션이 뭔지 간단히 짚고 가겠습니다.트랜잭션(Transaction)은 "더 이상 쪼갤 수 없는 작업의 단위"입니다. 은행 송금을 예로 들면:1. A 계좌에서 10만원 출금2. B 계좌에 10만원 입금 이 두 작업은 반드시 함께 성공하거나, 함께 실패해야 합니다. 1번만 성공하고 2번..

AOP(관점 지향 프로그래밍): 어떻게 메소드 실행 전후에 로그를 남길까?

Spring으로 개발하다 보면 이런 상황을 마주칩니다. 모든 서비스 메소드의 실행 시간을 측정하고 싶은데, 메소드가 50개라면 50군데에 똑같은 코드를 넣어야 할까요? 로그인 체크를 모든 메소드에 넣어야 한다면? 이런 반복적인 코드를 어떻게 깔끔하게 처리할 수 있을지 알아보겠습니다.문제 상황: 코드가 여기저기 중복된다실행 시간을 측정하는 코드를 직접 넣어보겠습니다.@Servicepublic class UserService { public User findById(Long id) { long start = System.currentTimeMillis(); // 측정 시작 // 실제 비즈니스 로직 User user = userRepository.findById(i..

스프링의 Scope(스코프): 싱글톤(Singleton)과 프로토타입(Prototype)의 결정적 차이

@Service로 등록한 Bean을 여러 곳에서 주입받으면, 같은 객체일까요 다른 객체일까요? 당연히 같은 객체입니다. 그런데 왜 같은 객체인지, 다른 객체로 만들 수는 없는지 생각해본 적 있으신가요?이 질문의 답이 바로 스코프(Scope)입니다. 스프링이 Bean을 어떻게 생성하고 관리할지 결정하는 설정인데, 이걸 모르고 사용하면 예상치 못한 버그를 만날 수 있습니다.스코프(Scope)란?좀 더 정확히 말하면, 스코프는 Bean이 존재할 수 있는 범위입니다. 스프링 컨테이너가 Bean 객체를 언제 생성하고, 얼마나 오래 유지할지를 결정합니다.스프링은 다양한 스코프를 지원하지만, 가장 기본이 되는 두 가지가 있습니다:싱글톤(Singleton): 컨테이너에 딱 하나만 존재프로토타입(Prototype): 요..

Filter vs Interceptor: 둘 다 거름망인데 도대체 뭐가 다를까?

Spring으로 웹 애플리케이션을 개발하다 보면 Filter와 Interceptor를 자주 마주칩니다. 둘 다 요청을 가로채서 뭔가를 처리한다는 점에서 비슷해 보입니다. 인증 처리를 해야 할 때, 로깅을 해야 할 때, 어떤 걸 써야 할지 고민이 될 때가 있습니다. 왜 두 가지가 따로 존재하는지, 각각 언제 사용하면 좋은지 정리해보겠습니다.핵심 차이: 누구의 영역인가결론부터 말하면, Filter는 Servlet의 영역이고, Interceptor는 Spring의 영역입니다.이 흐름을 보면 감이 올 겁니다. Filter가 먼저 실행되고, 그 다음에 Spring 영역으로 들어와서 Interceptor가 실행됩니다.Filter: Servlet 스펙Filter는 Java Servlet 스펙에 정의된 기능입니다. S..

DispatcherServlet의 역할: 요청이 들어와서 응답이 나갈 때까지의 여정

Spring MVC로 웹 애플리케이션을 개발하다 보면 "DispatcherServlet"이라는 이름을 한 번쯤 들어보셨을 겁니다. Controller만 잘 작성하면 알아서 동작하니까 굳이 알 필요가 있나 싶을 수도 있습니다. 하지만 이 녀석이 어떻게 동작하는지 알면, 에러가 발생했을 때 어디서 문제가 생겼는지 파악하기가 훨씬 쉬워집니다. 오늘은 HTTP 요청이 들어와서 응답이 나갈 때까지 어떤 일이 벌어지는지 따라가 보겠습니다.DispatcherServlet이 뭔가요? DispatcherServlet은 이름 그대로 "배달하는(Dispatch) 서블릿"입니다. 쉽게 말하면 모든 요청을 가장 먼저 받아서 적절한 곳으로 배달해주는 중앙 관제탑 같은 역할을 합니다.비유를 하나 들어보겠습니다.큰 회사의 안내 데스..

Spring Bean의 생명주기(Lifecycle) - 객체 생성부터 소멸까지

Spring을 사용하다 보면 "Bean이 언제 생성되고 언제 사라지는 걸까?"라는 궁금증이 생길 때가 있습니다. 특히 데이터베이스 연결이나 네트워크 소켓처럼 애플리케이션 시작 시 미리 연결해두고, 종료 시 안전하게 끊어야 하는 리소스를 다룰 때 이 생명주기를 정확히 이해하는 것이 중요합니다.처음 Spring을 배울 때는 그냥 @Component나 @Service를 붙이면 알아서 다 해주겠거니 했는데, 실제로 운영 환경에서 리소스 누수 문제를 겪고 나서야 생명주기의 중요성을 깨닫게 되었습니다.Spring Bean의 생명주기란?Spring Bean은 스프링 컨테이너가 관리하는 자바 객체입니다. 일반적인 자바 객체는 new 키워드로 생성하고 참조가 사라지면 GC(Garbage Collector)가 알아서 정리..