RESTful API를 설계할 때는 자원을 URI로 명확하게 표현하고, 행위를 HTTP 메서드로 구분해 의미에 맞게 사용하는 것이 중요합니다.
예를 들어, 사용자 정보를 가져올 때는 GET /users/{id}
처럼 명사 기반의 URI를 사용하고, POST
는 자원 생성, PUT
은 전체 수정, PUT
는 일부수정, DELETE
는 삭제로 사용하는 것이 RESTful한 방식입니다.
또한, URI에는 동사를 사용하지 않는 것, 상태 코드는 의도에 맞게 응답하는 것, 응답 포맷은 일관성 있게 JSON 등으로 제공하는 것이 중요한 포인트입니다.
꼬리질문:
PATCH
는 부분 수정이기 때문에, 각 필드가 존재할 때만 업데이트해야 함PUT
보다 코드량과 로직 분기가 많아짐PATCH
는 유연하지만, 명확한 구조와 검증이 어려움Spring MVC에서는 클라이언트 요청이 들어오면 DispatcherServlet이 가장 먼저 요청을 받아 처리 흐름을 시작합니다.
DispatcherServlet은 요청 URL에 따라 적절한 Controller를 찾고, 그 내부에서 Service, Repository 등을 거쳐 비즈니스 로직이 수행됩니다.
처리 결과는 다시 DispatcherServlet으로 전달되어, 필요시 View로 렌더링되거나 JSON 응답으로 클라이언트에 반환됩니다.
즉, 전체 흐름은 [DispatcherServlet → Controller → Service → Repository → DB] → 응답 처리 순서로 이루어집니다.
Spring에서는 JSON 데이터를 주고받기 위해 @RequestBody
와 @ResponseBody
를 주로 사용합니다.
클라이언트로부터 받은 JSON은 @RequestBody
를 통해 자바 객체로 변환하고, 서버에서 응답할 객체는 @ResponseBody
를 통해 JSON으로 직렬화됩니다.
이 과정은 내부적으로 Jackson 라이브러리가 자동으로 처리해주며, Spring Boot에서는 기본적으로 설정돼 있어서 별도의 설정 없이도 편리하게 사용할 수 있었습니다.
꼬리질문:
Jackson의 ObjectMapper를 활용한 JSON 커스터마이징을 진행했습니다.
@JsonInclude(JsonInclude.Include.NON_NULL)
을 이용해 불필요한 null 필드를 제거해 캐시 공간 절약하고, JSON 응답을 간결하게 만들었습니다.
또한 @JsonValue
와 @JsonCreator
를 사용해 직렬화 방식을 변경해 enum 타입은 사용자 친화적인 값으로 JSON에 노출되도록 했습니다.
200 OK
, 201 Created
가 있습니다.400 Bad Request
, 401 Unauthorized
, 404 Not Found
등이 있습니다.500 Internal Server Error
가 있습니다.꼬리질문:
상태 코드를 enum으로 관리해서 코드 번호, 메시지, 대응하는 HTTP 상태 코드를 한 곳에서 정의했습니다.
예를 들어 ErrorCode.INVALID_INPUT(400, "입력값이 유효하지 않습니다")
처럼 구성해서 공통 응답 포맷에 적용했습니다.
가장 고민했던 부분은 400 Bad Request
와 401 Unauthorized
의 구분이었습니다.
HTTP 스펙상으로는 비밀번호가 틀렸을 때 401을 반환하는 것이 맞지만,
실무에서는 401을 사용하면 오히려 “아이디는 맞고 비밀번호만 틀렸다”는 식의 힌트를 공격자에게 줄 수 있다는 보안상 우려가 있었습니다.
그래서 "아이디 불일치, 비밀번호 불일치, 입력값 오류” 같은 로그인 실패 상황을 모두 400 Bad Request로 통일해서 처리하기로 했습니다.
대신, 내부적으로는 AUTH_001, AUTH_002처럼 에러 코드와 메시지를 분리한 커스텀 응답 구조를 설계해서
프론트엔드가 실패 원인을 구분해서 처리할 수 있도록 했습니다.
사용자에게는 항상 “아이디 또는 비밀번호가 일치하지 않습니다.”처럼 안전하고 일반적인 메시지만 노출되도록 조정했습니다.
Refresh Token 서버 저장 (예: Redis)
Refresh Token은 클라이언트에만 보관하지 않고, 서버(Redis)에 사용자 ID 기준으로 저장합니다.
토큰 재발급 요청 시, 서버에 저장된 토큰과 비교해 일치할 경우에만 발급 처리합니다.
비밀번호 변경 시 Refresh Token 삭제
사용자가 비밀번호를 변경하면, 해당 계정의 모든 Refresh Token을 서버에서 삭제합니다.
이렇게 하면 다른 디바이스에서 로그인 상태라도, 이후 재발급이 불가능해집니다.
Access Token 블랙리스트 처리
Access Token은 stateless해서 원래는 서버에서 제어가 불가능하지만,
탈취가 의심되는 경우에는 해당 토큰을 Redis 블랙리스트에 저장해 차단합니다.
요청 처리 시, 블랙리스트 확인
클라이언트가 Access Token으로 요청할 때, 먼저 Redis에 블랙리스트 여부를 확인하고, 등록된 경우 401 Unauthorized 응답을 반환합니다.
Spring에서는 외부 API 호출 시 주로 RestTemplate
또는 WebClient
를 사용합니다.
전통적으로는 RestTemplate
이 많이 사용되며, 동기 방식으로 간단한 호출에 적합합니다.
반면, WebClient
는 Spring 5부터 등장한 비동기 방식의 클라이언트로, 비동기 처리나 리액티브 프로그래밍이 필요할 때 적합합니다.
실제로 제가 작업했던 프로젝트에서는 RestTemplate
을 사용해 API를 호출하고, 응답을 파싱해 클라이언트에 전달하는 기능을 구현한 경험이 있습니다.