백엔드 개발을 하면서 새롭게 배우고 적용하고 있던 것들 정리
무턱대고 만드는 것보다 이해하고 적용하는 게 재밌는 것 같긴 하다.
그리고 이해할 때 ai 사용하면 훨씬 빠르게 이해가 가능한 것 같다.
에러 코드는 서버에서 발생한 예외나 비정상 상황을 정형화된 숫자나 문자열로 표현한 값
이 코드는 클라이언트에게 오류의 원인을 전달하고, 자동화된 예외 처리를 가능하게 함.
에러 코드 | 메시지 설명
10001 잘못된 요청입니다. 공통 유효성 실패
10002 필수 입력 항목이 누락되었습니다. DTO의 NotBlank 등
20001 사용자를 찾을 수 없습니다. User 조회 실패
20002 이메일과 비밀번호는 필수 입력입니다. 로그인 입력 누락
① ErrorCode Enum 정의
@Getter
public enum ErrorCode {
INVALID_REQUEST(10001, "잘못된 요청입니다."),
MISSING_REQUIRED_FIELD(10002, "필수 입력 항목이 누락되었습니다."),
USER_NOT_FOUND(20001, "사용자를 찾을 수 없습니다."),
...
SERVER_ERROR(90001, "서버 내부 오류가 발생했습니다.");
private final int code;
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
}
② ApiResponse 응답 형식
{
"success": false,
"data": null,
"message": "사용자를 찾을 수 없습니다.",
"errorCode": 20001
}
③ CustomException으로 throw
throw new CustomException(ErrorCode.USER_NOT_FOUND);
④ GlobalExceptionHandler에서 일괄 처리
@ExceptionHandler(CustomException.class)
public ResponseEntity<ApiResponse<Void>> handleCustomException(CustomException ex) {
ErrorCode errorCode = ex.getErrorCode();
return ResponseEntity
.ok(ApiResponse.error(errorCode.getMessage(), errorCode.getCode()));
}
에러 발생 위치 및 종류를 명확히 식별 가능
클라이언트 자동화 처리 및 메시지 분기 처리 용이
실제 HTTP 상태는 항상 200 OK, 문제는 errorCode로 표현 → 프론트엔드, 앱에서 일관된 응답 구조 확보
에러 코드는 도메인 단위로 정리되었는가?
각각의 에러 코드가 명확한 의미를 가지고 있는가?
클라이언트에서 코드 기준으로 분기 처리가 가능한가?
코드 충돌을 방지하기 위한 번호 체계가 잘 정의되어 있는가?
| 항목 | accessToken | refreshToken |
|---|---|---|
| 목적 | 인증(Authorization) | accessToken 재발급 |
| 수명 | 짧음 (15분~1시간) | 김 (7일~30일) |
| 저장 위치 | 클라이언트 메모리, 로컬스토리지 | 보통 HttpOnly 쿠키 또는 DB |
| 보안 | 탈취 시 위험 → 짧게 유지 | 탈취 시 무한 발급 가능 → 안전하게 저장 필요 |
2) refreshToken 생성
3) refreshToken DB에 저장
4) 둘 다 클라이언트에 전달
2) 서버가 유효성 검사 후 새 accessToken 발급
com.example.app
├── controller ← AuthController
├── dto ← LoginRequestDTO, LoginResponseDTO
├── security ← JwtProvider
├── service ← UserService or AuthService
├── repository ← RefreshTokenRepository
└── entity ← RefreshToken
🔹 JwtProvider.java
public String generateAccessToken(User user);
public String generateRefreshToken(User user);
🔹 UserService.java or AuthService.java
public String loginUser(String email, String password); // accessToken 발급
public String generateAndStoreRefreshToken(User user); // refreshToken 발급 + 저장
🔹 AuthController.java
@PostMapping("/login")
public ResponseEntity<?> loginUser(...) {
accessToken = userService.loginUser(...);
refreshToken = userService.generateAndStoreRefreshToken(...);
return ResponseEntity.ok(new LoginResponseDTO(..., accessToken, refreshToken));
}
@Entity
public class RefreshToken {
@Id @GeneratedValue
private Long id;
private Long userId;
private String token;
private LocalDateTime expiryDate;
}
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
Optional<RefreshToken> findByToken(String token);
void deleteByUserId(Long userId);
}
POST /auth/refresh
→ refreshToken을 검증 후 새 accessToken 반환