우대사항 : Kotlin 기반 서비스 개발 경험
저는 IT 기업들이 최근 요구하는 스택은 무엇인지 계속 인지하기 위해 채용공고들을 꽤 자주 보는편입니다.
최근 백엔드 개발자 채용 공고, 특히 빅테크 IT 기업이나 성장하는 스타트업의 백엔드 직무 자격요건을 보다 보면 눈에 띄는 공통점이 있습니다.
Java 기반의 Spring Boot 가 여전히 메인이긴 하지만 우대 사항이나 자격 요건에 Kotlin + Spring Boot 환경 개발 이 심심치 않게, 아니 꽤 자주 등장한다는 점입니다.
단순히 새로운 언어가 유행이라서일까요 아니면 확실한 기술적 이점이 있기 때문일까요?
궁금증이 생겨 Java와 Kotlin의 코드를 직접 비교해보며 왜 기업들이 점점 Kotlin을 선호하는지 알아보았습니다!
1. 생산성의 차이 [Data Class]
가장 먼저 체감되는 것은 코드의 간결함입니다. Java로 개발할 때 우리는 DTO 하나를 만들기 위해 꽤 많은 반복 노동을 해야 했습니다. 물론 Lombok이라는 훌륭한 라이브러리가 있지만 언어 차원에서 지원하는 것과는 다릅니다.
Java (Lombok)
@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor
public class UserDto {
private String name;
private int age;
private String email;
}
Kotlin
data class UserDto(
val name: String,
val age: Int,
val email: String
)
Kotlin의 data class는 생성자, Getter, equals(), hashCode(), toString()을 무려 '컴파일 시점'에 자동으로 생성해 줍니다. 코드가 짧아진다는 것은 단순히 타자 칠 일이 줄어든다는 것을 넘어, "읽어야 할 코드가 줄어들어 비즈니스 로직에 더 집중할 수 있다" 는 큰 장점이 됩니다.
또한, val 키워드를 통해 기본적으로 불변성을 유지하기 쉽고 copy() 메서드를 통해 데이터 변경 시 안전하게 새 객체를 생성하는 패턴도 언어 레벨에서 지원합니다.
2. Null Safety
Java 개발자라면 누구나 한 번쯤 NullPointerException 때문에 로그를 뒤져본 경험이 있을 것입니다. Java에서는 컴파일 시점에 변수에 null이 들어갈지 아닐지 확신할 수 없습니다. 그래서 방어 로직이 덕지덕지 붙거나 런타임에 에러가 터지곤 합니다.
Kotlin은 이 문제를 컴파일 단계에서 원천 차단합니다.
Java
public void printLength(String str) {
// 실수로 null 체크를 안 했다면? -> NPE 발생!
System.out.println(str.length());
}
Kotlin
// String? 은 null이 들어올 수 있음을 명시
fun printLength(str: String?) {
// 컴파일 에러! null 가능성이 있는 변수는 바로 사용할 수 없음
// println(str.length)
// 안전한 호출 (?.)
println(str?.length)
}
Kotlin 컴파일러는 null이 될 수 있는 타입(Type?)과 아닌 타입(Type)을 철저히 구분합니다.
3. 비동기 처리 [Coroutines]
최근 대용량 트래픽 처리를 위해 비동기 논블로킹 방식인 Spring WebFlux를 도입하는 사례가 늘고 있습니다.
하지만 Java의 Reactor 패턴(Mono, Flux)은 러닝커브가 가파르고 가독성이 또한 떨어집니다.
Java (Reactor)
public Mono<Delivery> getDelivery(String userId) {
return userRepository.findById(userId)
.flatMap(user -> deliveryRepository.findByUserNo(user.getUserNo()))
.subscribeOn(Schedulers.boundedElastic());
}
흐름을 파악하려면 flatMap 안으로 시선을 옮기며 머릿속으로 로직을 재구성해야 합니다.
Kotlin (Coroutines)
suspend fun getDelivery(userId: String): Delivery {
val user = userRepository.findById(userId) ?: throw UserNotFoundException()
return deliveryRepository.findByUserNo(user.userNo)
}
Kotlin의 Coroutine을 사용하면 suspend 키워드 하나로 비동기 코드를 마치 동기 코드(순차적 코드)처럼 작성할 수 있습니다. 가독성은 압도적으로 좋아지고 디버깅도 훨씬 쉽습니다.
고성능이 필요한 MSA 환경에서 Kotlin이 Java보다 유리한 이유 중 하나 입니다.
4. 마이그레이션 맛보기
제가 최근에 진행했던 토이 프로젝트의 코드를 직접 마이그레이션 해보았습니다.
Github API를 통해 커밋 내역을 가져와 DTO로 변환하는 GithubService의 로직입니다.
Java (Before)
ArrayList를 만들고, for 문을 돌며 데이터를 꺼내 add 합니다.
Getter 호출(getCommitShortInfo())이 중첩되어 코드가 길어집니다.
public List<CommitResponse> getCommits(String token, String repoName) throws IOException {
GitHub github = new GitHubBuilder().withOAuthToken(token).build();
GHRepository repo = github.getRepository(repoName);
List<CommitResponse> commits = new ArrayList<>();
// 반복문과 리스트 추가 로직
for (GHCommit commit : repo.queryCommits().pageSize(20).list()) {
commits.add(new CommitResponse(
commit.getSHA1(),
commit.getCommitShortInfo().getMessage(),
commit.getCommitShortInfo().getAuthor().getName(),
commit.getCommitDate().toString()
));
}
return commits;
}
Kotlin (After)
Kotlin의 컬렉션 함수인 map을 활용했습니다.
new키워드가 사라졌습니다.getSHA1()대신sha1처럼 프로퍼티 접근 문법을 사용하여 가독성이 좋아졌습니다.- 명령형(for-loop)이 아닌 선언형(map) 스타일로 데이터 흐름이 한눈에 들어옵니다.
fun getCommits(token: String, repoName: String): List<CommitResponse> {
val github = GitHubBuilder().withOAuthToken(token).build()
val repo = github.getRepository(repoName)
// map을 사용해 변환 로직을 간결하게 표현
return repo.queryCommits().pageSize(20).list()
.map { commit ->
CommitResponse(
sha = commit.sha1,
message = commit.commitShortInfo.message,
author = commit.commitShortInfo.author.name,
date = commit.commitDate.toString()
)
}
}
동일한 로직이지만, Kotlin으로 작성했을 때 불필요한 코드가 줄어들고 **"데이터를 가져와서 변환한다"**는 본질적인 로직만 남는 것을 확인할 수 있었습니다.
5. Java와의 100% 상호 운용성!!
이 모든 장점에도 불구하고, 기존 Java 코드를 다 버려야 한다면 기업들은 도입을 망설였을 겁니다. 하지만 Kotlin은 Java와 100% 호환됩니다.
- 하나의 프로젝트 안에
.java파일과.kt파일이 공존할 수 있습니다. - Java로 짜인 방대한 라이브러리(JPA, Spring Security 등)를 그대로 가져다 쓸 수 있습니다.
- 무리하지 않는 선에서의 점진적인 마이그레이션이 가능합니다.
마무리하며
어쩌다 졸작을 Android 개발로 했던 저에게 Kotlin이 크게 낯설진 않았지만 이전의 저에게 Kotlin은 그저 Android 개발만을 위한 언어였습니다.
그러나 이번 기회에 직접 비교해보고 사용해보니 어떤 부분에서 백엔드 개발자들이 매력을 느끼며 왜 Kotlin을 선호하는지 명확해졌습니다.
물론 Java도 계속 발전하고 있지만, Spring 프레임워크 자체가 Kotlin을 First-class 언어로 지원하고 있는 만큼, 백엔드 개발자로서 Kotlin은 이제 '선택'이 아닌 '필수' 역량으로 다가오는 것 같습니다.