BlueNyang
[Kotlin Spring Boot] 롬복(Lombok) 버리기 - OAuth2 핸들러 리팩토링
Spring

[Kotlin Spring Boot] 롬복(Lombok) 버리기 - OAuth2 핸들러 리팩토링

BlueNyangBlueNyang
·
·
약 4분
·
# oauth2-success-handler# primary-constructor# safe-casting# expression-statement

지난 글에서는 영속성 계층에서 AttributeConverter를 변환할 때 겪을 수 있는 데이터베이스의 널(Null) 정합성 함정과 코틀린의 안전한 방어 기제에 대해 알아보았습니다.

이번 글에서는 인증 및 인가 로직의 핵심인 OAuth2 로그인 성공 핸들러(Success Handler)를 리팩토링하는 과정을 다룹니다. 자바(Java) 진영에서 숨 쉬듯이 사용하던 롬복(Lombok)을 걷어내고, 코틀린(Kotlin) 고유의 문법과 불변성(val)을 통해 코드를 얼마나 안전하게 만들 수 있는지 살펴볼 것입니다. 이 글에서는 제가 마이그레이션을 하며 겪은 실수와 함께 코틀린이 제공하는 안전장치를 소개하고자 합니다.

Note
해당 글은 Kotlin Spring Boot 3.x 및 JDK 21 환경을 기준으로 작성되었습니다.

1. 롬복(Lombok)과의 이별과 주 생성자 주입

스프링 부트 환경에서 자바 코드를 작성할 때, 의존성 주입을 위해 @RequiredArgsConstructorprivate final 조합을 사용하는 것은 하나의 공식과도 같았습니다.

1.1. 언어 레벨에서 지원하는 의존성 주입

코틀린은 주 생성자(Primary Constructor)라는 강력한 기능을 통해 롬복의 도움 없이도 훨씬 깔끔한 의존성 주입을 지원합니다. 초창기 마이그레이션 과정에서 자바의 습관대로 var(가변 변수)를 사용하여 의존성을 주입하는 실수를 할 수 있습니다.

kotlin
// 실수: 의존성을 var로 선언하여 불변성이 깨질 위험이 있음
@Component
class OAuth2LoginSuccessHandler(
    private var jwtTokenProvider: JwtTokenProvider,
    private var oAuth2Properties: OAuth2Properties
) : SimpleUrlAuthenticationSuccessHandler() { ... }

스프링의 싱글톤 빈(Bean)으로 등록되는 컴포넌트는 애플리케이션의 생명주기 동안 그 의존성이 절대 변경되어서는 안 됩니다. 따라서 var 대신 코틀린의 읽기 전용 속성인 val을 사용하여 자바의 final과 동일한 불변성을 컴파일 타임에 보장받아야 합니다.

kotlin
// 개선: 롬복 없이 주 생성자와 val을 활용한 완벽한 불변성 보장
@Component
class OAuth2LoginSuccessHandler(
    private val jwtTokenProvider: JwtTokenProvider,
    private val oAuth2Properties: OAuth2Properties
) : SimpleUrlAuthenticationSuccessHandler() { ... }

2. 잠재적 런타임 에러 방지와 안전한 캐스팅

OAuth2 제공자(Kakao, Google 등)로부터 받아온 사용자 정보(Attributes)를 파싱하는 과정은 Map 구조를 다루기 때문에 타입 캐스팅 에러와 NullPointerException이 발생하기 가장 쉬운 지점입니다.

2.1. "null" 문자열이 DB에 저장되는 버그

자바 코드의 타입 캐스팅을 코틀린으로 단순 변환할 때, 아래와 같이 아찔한 실수를 저지를 수 있습니다.

kotlin
// 위험한 코드: kakaoId가 null일 경우 "null"이라는 4글자 문자열이 반환됨
val attributes = oAuth2User.attributes
val kakaoId = attributes["id"] as Long?
val providerId = kakaoId.toString() // 만약 kakaoId가 null이라면? -> "null"

위 코드에서 kakaoId가 실제로 null일 경우, 코틀린의 toString()은 이를 에러로 터뜨리지 않고 문자열 "null"로 변환해버립니다. 이 값이 그대로 데이터베이스에 저장된다면 추후 치명적인 비즈니스 로직 오류를 낳게 됩니다.

2.2. 안전한 캐스팅(as?)과 빠른 실패(Fail-Fast)

코틀린은 이런 상황을 위해 '안전한 캐스팅 연산자(as?)'를 제공합니다. 타입 캐스팅에 실패하면 예외를 던지는 대신 안전하게 null을 반환합니다. 여기에 엘비스 연산자(?:)를 결합하여 필수 값이 없을 경우 즉시 예외를 발생시키는 방어 로직을 구축할 수 있습니다.

kotlin
// 안전한 캐스팅과 명확한 예외 처리
val kakaoId = (oAuth2User.attributes["id"] as? Long)?.toString()
    ?: throw IllegalArgumentException("카카오 ID를 찾을 수 없습니다.")

이 안전장치 덕분에 예상치 못한 "null" 문자열의 저장을 원천 차단하고, 오류 상황을 명확하게 통제할 수 있게 됩니다.

3. 표현식(Expression)을 활용한 제어 흐름

자바에서 if-else는 어떤 동작을 수행하는 '명령문(Statement)'이지만, 코틀린에서는 결과를 반환하는 '표현식(Expression)'입니다.

3.1. 변수 선언과 분기의 결합

로그인한 유저가 신규 가입자인지 기존 유저인지에 따라 리다이렉트할 URL이 달라지는 로직을 자바로 짠다면, 보통 빈 String 변수를 먼저 선언하고 if-else 블록 안에서 값을 재할당하게 됩니다. 하지만 코틀린은 if-else 자체를 값처럼 다룰 수 있습니다. 즉, 코틀린의 if-else는 값을 생성할 수 있다는 뜻입니다.

kotlin
// if-else 표현식을 통해 불필요한 상태 변경(var)을 막고 즉시 val에 할당
val targetUrl = if (user == null) {
    val tempJwt = jwtTokenProvider.createTemporaryAccessToken(...)
    UriComponentsBuilder.fromUriString(oAuth2Properties.signupUri)
        .queryParam("temp_token", tempJwt)
        .build().toUriString()
} else {
    val jwt = jwtTokenProvider.createAccessToken(...)
    UriComponentsBuilder.fromUriString(oAuth2Properties.redirectUri)
        .queryParam("token", jwt)
        .build().toUriString()
}

redirectStrategy.sendRedirect(request, response, targetUrl)

이와 더불어 코틀린의 후행 람다(Trailing Lambda) 규칙을 적용하면, .orElseGet({ ... })와 같이 장황했던 자바의 Optional 처리 로직을 .orElseGet { ... } 형태로 훨씬 읽기 쉽게 다듬을 수 있습니다.

4. 마무리하며

처음 멀티캠퍼스(Multicampus) 프로젝트의 백엔드 전환을 시작할 때만 해도, 제게 코틀린은 그저 '자바보다 세련된 언어' 정도였습니다. 하지만 롬복(Lombok)을 걷어내고 주 생성자로 의존성을 주입하며, val과 안전한 캐스팅(as?), 그리고 표현식(Expression)을 손에 익히는 과정은 상당히 흥미로웠고, 설계를 바라보는 여러 가지 관점을 배우게 되었습니다.

특히 이번 OAuth2 핸들러 리팩토링은 언어의 문법이 소프트웨어의 설계에 얼마나 깊이 관여하는지 체감하는 계기가 되었습니다. null.toString() 같은 잠재적 런타임 에러를 컴파일 타임과 언어 레벨의 안전장치로 막아내면서, 단순히 기능이 돌아가는 코드가 아니라 '예측 가능하고 엣지 케이스에 무너지지 않는 안전한 아키텍처'를 고민하는 시야를 갖게 되었습니다. 이제는 새로운 로직을 짤 때마다 어떻게 하면 불변성을 더 엄격하게 유지할 수 있을지부터 고민하게 됩니다.

무엇보다 이 모든 마이그레이션 과정이 정말 '재밌었습니다'. 단순히 익숙한 자바 코드를 다른 언어로 기계적으로 번역하는 것이 아니라, 코틀린이 제공하는 불변성과 널 안전성이라는 룰 안에서 더 단단하고 깔끔한 코드를 조각해 나가는 퍼즐을 푸는 기분이었습니다. 동작이 예측 가능하고 안정적인 Kotlin이 제 개발 철학에 완벽히 부합하는 언어라는 확신이 들었고, 만약 새로운 Spring Boot 프로젝트를 시작한다면 주저 없이 Kotlin을 선택할 것 같습니다.

저처럼 자바에서 코틀린으로의 생소한 전환을 마주하고 계신 분들이 있다면, 이런 관점들에서 재미를 찾아볼 수 있기를 바랍니다.

BlueNyang
작성자BlueNyang
라이선스
CC BY NC
BlueNyang

BlueNyang

BlueNyang의 개발 log

카테고리

  • Development
  • Framework
  • Language
  • Dev Tools
  • DevOps & Infra
  • Studies

페이지

© 2026 BlueNyang. All rights reserved.

Made with Nuxt.js and Directus