새롭게 들어가는 ‘빌릴게’ 프로젝트에서 혼자 백엔드를 맡게 되어 ‘이참에 Kotlin으로 플젝을 진행해보자!’라는 생각으로 Kotlin + Spring 조합으로 스택을 구성했다.

Kotlin은 자바처럼 JVM 위에서 동작하는 언어지만, 자바보다 간결하고 쓸데없이 적어야하는 코드가 적다!

그리고, 언어 자체에 안정성이 높아서 null 가능성 체크를 통해 NullPointerException을 미리미리 방지할 수 있다고 한다..!

실제로 작성한 자바 코드와 코틀린 코드를 아래처럼 비교했을 때 더 읽기 편한 코드는 누가 봐도 코틀린이다.

(아닌가..?)

package cokothon.backend.global.jwt;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Arrays;

@RequiredArgsConstructor
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private final TokenProvider tokenProvider;
    private final static String HEADER_AUTHORIZATION = "Authorization";
    private final static String BEARER_AUTH = "Bearer ";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authorizationHeader = request.getHeader(HEADER_AUTHORIZATION);
        String accessToken = getAccessToken(authorizationHeader);

        if (tokenProvider.validToken(accessToken)) {
            Authentication authentication = tokenProvider.getAuthentication(accessToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);
    }

    private String getAccessToken(String authorizationHeader) {
        if (authorizationHeader != null && authorizationHeader.startsWith(BEARER_AUTH)) {
            return authorizationHeader.substring(BEARER_AUTH.length());
        }

        return null;
    }
}
package site.billilge.api.backend.global.security.jwt

import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.filter.OncePerRequestFilter

class TokenAuthenticationFilter(
    private val tokenProvider: TokenProvider
): OncePerRequestFilter() {
    override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
        val authorizationHeader = request.getHeader(HEADER_AUTHORIZATION)

        authorizationHeader?.let {
            val accessToken = getAccessToken(authorizationHeader);

            if (tokenProvider.validToken(accessToken)) {
                SecurityContextHolder.getContext().authentication = tokenProvider.getAuthentication(accessToken)
            }
        }
    }

    private fun getAccessToken(authorizationHeader: String): String {
        if (authorizationHeader.startsWith(BEARER_AUTH)) {
            return authorizationHeader.substring(BEARER_AUTH.length)
        }

        //TODO: exception handling 만들기
        throw Exception()
    }

    companion object {
        const val HEADER_AUTHORIZATION: String = "Authorization"
        const val BEARER_AUTH: String = "Bearer "
    }
}

또한, @RequiredArgsConstructor같은 Lombok 어노테이션 사용이 줄어든다. 자바에서는 boilerplate한 코드를 줄이기 위해 Lombok 라이브러리의 Getter, Setter 같은 어노테이션을 많이 사용했었는데, 코틀린은 getter, setter 함수를 따로 만들 필요 없이 호출이 가능해서 너무 좋은 것 같다.

결론

코틀린 짱