기본 콘텐츠로 건너뛰기

라벨이 java인 게시물 표시

스프링 MVC 응답 처리 이해하기: `@ResponseBody`, `ResponseEntity`, 그리고 뷰 반환의 차이

스프링 MVC 응답 처리 이해하기: @ResponseBody , ResponseEntity , 그리고 뷰 반환의 차이 스프링 MVC에서 컨트롤러 메서드는 결국 값을 return 합니다. 그런데 같은 return 이어도 어떤 경우에는 JSON이 내려가고, 어떤 경우에는 템플릿이 렌더링됩니다. 예를 들어 return "users/detail"; 는 화면 이름처럼 동작할 수도 있고, 응답 본문에 문자열 그대로 기록될 수도 있습니다. 이 차이는 자바 문법이 아니라 스프링 MVC의 반환값 해석 방식에서 생깁니다. 핵심은 컨트롤러가 무엇을 반환했는지가 아니라, 스프링이 그 반환값을 어떤 의미로 받아들이는지입니다. @ResponseBody 가 붙어 있는지, ResponseEntity<T> 를 반환하는지, 일반 @Controller 인지에 따라 내부 처리 경로가 달라집니다. 왜 같은 return 문인데 결과가 달라질까 가장 먼저 정리할 기준은 이것입니다. @ResponseBody 가 있으면 반환값은 HTTP 응답 본문으로 처리됩니다. ResponseEntity<T> 를 반환하면 응답 본문, 상태 코드, 헤더까지 함께 처리됩니다. 위 조건이 없고 @Controller 에서 String 을 반환하면 보통 뷰 이름으로 해석됩니다. 즉, 아래 두 코드는 문법은 비슷하지만 의미가 완전히 다릅니다. @Controller public class PageController { @GetMapping("/hello-page") public String hello() { return "hello"; } } 이 경우 "hello" 는 응답 문자열이 아니라 뷰 이름입니다. 스프링은 hello.html , hello.jsp 같은 실제 뷰를 찾으려 합니다. 반대로 다음 코드는 "hello" 를 응답 본문에 직접 씁니다. @Co...

Spring MVC에서 `@ExceptionHandler`를 제대로 쓰는 방법: 예외 흐름부터 전역 처리까지

Spring MVC에서 @ExceptionHandler 를 제대로 쓰는 방법: 예외 흐름부터 전역 처리까지 Spring MVC에서 예외 처리는 부가 기능이 아니라 API 설계의 일부입니다. 같은 실패 상황인데 어떤 엔드포인트는 400을 주고, 어떤 곳은 500을 주고, 어떤 응답은 메시지 형식조차 다르다면 클라이언트 코드도 복잡해지고 운영 중 원인 파악도 어려워집니다. @ExceptionHandler 는 이런 혼란을 줄이기 위한 핵심 도구입니다. 컨트롤러 실행 과정에서 발생한 예외를 잡아, 애플리케이션이 의도한 HTTP 상태 코드와 응답 본문으로 바꿔 주는 역할을 합니다. 단순히 예외를 숨기는 기능이 아니라, 예외를 API 계약에 맞는 응답으로 번역하는 장치라고 이해하면 훨씬 실무적입니다. 이 글에서는 @ExceptionHandler 가 정확히 어디서 동작하는지, @ControllerAdvice 와는 어떻게 역할을 나누는지, 그리고 실제로 어떤 응답 구조를 만들어야 유지보수가 쉬운지까지 한 번에 정리해보겠습니다. 왜 컨트롤러 예외 처리를 따로 설계해야 할까 예외는 없어지지 않습니다. 잘못된 요청 본문, 존재하지 않는 리소스, 현재 상태에서는 허용되지 않는 비즈니스 요청, 예상하지 못한 런타임 오류는 서비스가 살아 있는 한 계속 발생합니다. 중요한 것은 예외 발생 자체가 아니라, 그 예외가 어떤 HTTP 응답으로 변환되느냐입니다. 예외 처리를 설계하지 않으면 보통 다음 문제가 나타납니다. 사용자 입력 오류와 서버 내부 오류가 모두 500으로 보입니다. 프론트엔드가 엔드포인트마다 다른 실패 형식을 처리해야 합니다. 컨트롤러마다 try-catch 가 늘어나서 비즈니스 로직이 흐려집니다. 운영 로그에는 스택 트레이스만 남고, 클라이언트에게는 맥락 없는 오류 메시지만 전달됩니다. 실무에서는 예외를 “없애는 것”보다 “의미 있게 분류하는 것”이 더 중요합니다. 예를 들어 리소스 없음은 404, 요청 값 오류는 400, 상태 충돌은 409, 미처 고...

스프링 `@Value`가 기대대로 주입되지 않는 이유와 안전하게 사용하는 방법

스프링 @Value 가 기대대로 주입되지 않는 이유와 안전하게 사용하는 방법 스프링 부트에서 설정값을 읽을 때 가장 먼저 떠오르는 도구가 @Value("${...}") 입니다. 문법이 짧고 바로 쓸 수 있어서 편하지만, 동작 원리를 모른 채 남용하면 의외로 자주 문제를 만납니다. 어떤 클래스에서는 값이 잘 들어오는데 다른 곳에서는 null 처럼 보이거나, 애플리케이션 시작 시점에 Could not resolve placeholder 예외가 터지는 식입니다. 핵심은 하나입니다. @Value 는 임의의 객체에 값을 꽂아 넣는 기능이 아니라, 스프링 컨테이너가 관리하는 빈에 대해 정해진 생명주기 안에서 실행되는 주입 메커니즘입니다. 이 전제를 이해하면 @Value 가 잘 맞는 범위, 피해야 할 패턴, 그리고 언제 @ConfigurationProperties 로 넘어가야 하는지가 한 번에 정리됩니다. @Value 는 어떤 문제를 풀고, 어디까지 써야 할까 @Value 는 외부 설정에서 단일 값을 읽어올 때 가장 간단합니다. 예를 들어 API 기본 URL, 타임아웃, 기능 플래그처럼 "몇 개 안 되는 설정"을 빠르게 주입하는 데 적합합니다. application.yml , application.properties , 환경 변수, JVM 옵션, 커맨드라인 인자처럼 스프링 Environment 에 올라온 값이라면 대부분 읽을 수 있습니다. 반대로 관련 설정이 여러 개로 늘어나기 시작하면 @Value 의 약점이 드러납니다. 문자열 키가 서비스 클래스 곳곳에 흩어지고, 리스트나 중첩 구조를 다루려면 표현식이 복잡해지며, 설정 검증도 분산됩니다. 그래서 실무에서는 보통 다음 기준으로 나눕니다. 설정 1~2개를 빠르게 읽는다: @Value 하나의 도메인 설정을 묶어서 다룬다: @ConfigurationProperties 중요한 점은 @Value 가 "간단해서 무조건 좋은 방식"은 아니라는 것입니다. 단순한 ...

스프링 `@Value`를 안전하게 쓰는 방법: 주입 시점부터 설정 바인딩 선택 기준까지

스프링 @Value 를 안전하게 쓰는 방법: 주입 시점부터 설정 바인딩 선택 기준까지 스프링 부트에서 설정값을 코드 밖으로 빼는 일은 거의 필수에 가깝습니다. 외부 API 주소, 타임아웃, 토큰, 기능 플래그처럼 환경마다 달라지는 값을 소스 코드에 직접 넣어두면 배포와 운영이 빠르게 꼬입니다. 이때 가장 먼저 떠올리기 쉬운 도구가 @Value 입니다. 문제는 @Value 가 간단해 보인다는 점입니다. 한 줄로 값을 넣을 수 있다 보니 동작 조건과 주입 시점을 놓치기 쉽고, 설정이 커졌을 때도 계속 같은 방식으로 밀어붙이기 쉽습니다. @Value 는 분명 유용하지만, 어디까지가 적절한 사용 범위인지 알아두지 않으면 디버깅 비용이 커집니다. @Value 는 어떤 문제를 해결할까 @Value 는 외부 설정값 하나를 스프링 빈에 주입할 때 가장 빠르게 적용할 수 있는 방법입니다. 예를 들어 base-url , timeout , enabled 같은 단일 값을 서비스나 컴포넌트 안으로 가져와야 할 때 코드 양이 적고 이해하기 쉽습니다. 핵심은 @Value 가 “설정 한두 개를 직접 꺼내 쓰는 도구”라는 점입니다. 값이 적고 맥락이 단순하면 충분히 좋은 선택입니다. 반대로 같은 접두사 아래에 여러 설정이 묶여 있고, 그 설정들이 하나의 의미 있는 그룹을 이룬다면 @Value 보다 @ConfigurationProperties 가 더 자연스럽습니다. @Value 가 동작하는 조건과 주입 시점 가장 먼저 기억해야 할 사실은 @Value 가 스프링이 관리하는 객체에서만 동작한다는 점입니다. @Component , @Service , @Configuration 이 붙어 있거나 @Bean 으로 등록된 객체여야 합니다. 직접 new 로 생성한 객체에서는 스프링이 개입하지 않으므로 @Value 도 작동하지 않습니다. 주입 시점도 중요합니다. 생성자 파라미터에 붙인 @Value 는 객체를 만들기 전에 해석되어 전달됩니다. 반면 필드 주입과 세터 주입은 객체 생성 이후에 적...