본문 바로가기
  • Tech Log
  • Krew Insight
  • Tech Trend
  • Tech & News
  • About Us

카카오엔터프라이즈 기술블로그 Tech&(테크앤)

  • 글쓰기
  • 관리자
  • 로그인
  • 로그아웃
Tech&

카카오엔터프라이즈의 기술, 그리고 크루들의 이야기를 만나보세요

  • 페이스북
  • 유튜브
  • 인스타그램
    Krew Insight 2020. 11. 23.

    [KREW INSIDE] 신입 개발자의 좌충우돌 문제 해결기

    시작하며

    안녕하세요 저는 카카오엔터프라이즈 신입 개발자 harry 입니다. 3개월간의 인턴 과정을 마치고 전환되어, AI서비스플랫폼실 서버 개발팀 비즈플랫폼 파트 IAM 셀에서 근무하고 있습니다.

    제가 현재 소속되어 있는 AI서비스플랫폼실 서버 개발팀은 테스트 코드의 중요성을 강조하는 팀입니다. 사이드 이펙트를 배포 전에 발견하고 견고한 코드를 작성하기 위해서 테스트 코드를 잘 작성하는 것은 개발자의 기본이라고 생각합니다. 그러나 저와 같은 신입 개발자 입장에서 Spring Framework는 여전히 어렵고, JUnit이라는 테스트 프레임워크는 낯설기만 한데다가 Kotlin이라는 언어는 생소하기까지 합니다. 그래서 테스트 코드를 잘 작성하는 일이 쉽지 않았으며, 지금도 여전히 다양한 케이스를 커버하는 테스트 코드를 잘 작성하려고 노력하고 있습니다. 오늘은 제가 테스트 코드를 작성하며 어떤 문제가 발생했고 그 문제를 어떻게 해결했는지에 대해서 이야기해보려고 합니다.

     


    문제 상황

    테스트 코드에서 생성자를 이용하여 Bean을 주입받으려고 했으나 오류가 발생한 것이 문제의 발단이었습니다.

    아래는 설명을 위한 예제 테스트 코드입니다.

     

    [그림 1] 예제 테스트 코드

     

    Kotlin에서는 Java와는 달리 생성자를 아주 쉽게 정의할 수 있는데요, Intellij 또한 생성자 주입을 권장하고 있기 때문에 메인 코드에서 위와 같은 생성자로 Bean을 주입받을 수 있었습니다. 그러나 제가 작성한 테스트 코드에서 에러가 발생하였고 저는 원인을 몰라서 의아했습니다.

     

    [그림 2] 어딨니 콩아...

     

    심지어 기존 Intellij에서 Bean을 주입받는 경우 보이는 콩 모양 아이콘 또한 보이지 않았습니다. 이때 저는 느낌적인 느낌으로 Bean 주입이 안 되는 것이 원인이 아닐까 싶기는 했었지만 그래도 여전히 원인을 몰라 고민을 하고 있었습니다. 그때 같은 팀 개발자인 PJ가 지나가시다가 제가 고민하고 있는걸 보시더니 다음과 같이 코드를 수정해주셨습니다.

     

    [그림 3] 수정 테스트 코드

     

    놀랍게도 @Autowired constructor를 명시해주자 테스트 코드가 정상적으로 동작했으며 콩 모양도 잘 보이기 시작했습니다.

     

    [그림 4] 정상적으로 동작하는 모습

     

    환호도 잠시 저는 깊은 고민에 빠지기 시작했습니다. 

    왜 @Autowired constructor를 붙여야지만 Bean 주입이 되는 것인가..? 

    그렇습니다.

    여러분도 잘 아시는 개발자의 딜레마가 시작된 것이지요. 

     

    개발자라면 늘 겪는 문제인, 해결하고도 왜 해결이 됐는지 모르는 상황이 발생하고야 말았습니다. 퇴근 후 집에 돌아와 문제를 파헤치기 시작했고 나름의 답을 찾아서 지금부터 여러분들에게 공유를 드리고자 합니다. 그럼 지금부터 논리적으로 이 문제가 왜 발생했으며 왜 해결됐는지를 하나씩 살펴보도록 하겠습니다.

     

    에러 로그를 읽어보자

    PJ가 저의 문제를 함께 봐주시면서 이렇게 말씀해주셨습니다.

    “이렇게 에러가 나면 에러 로그를 잘 봐야해요 Harry, 여기에 답이 있어요.”

     

    실제 코드에서는 당시 Bean 주입 외에도 다른 여러가지 에러들이 많았기 때문에 에러 로그가 훨씬 많이 찍혔었고 저는 그 수 많은 에러를 일일이 확인할 엄두조차 나지  않았습니다. 그러나 PJ의 조언을 듣고 에러 로그를 침착하게 다시 읽기 시작했습니다. 아래의 에러 로그가 바로 이번 글의 문제를 해결할 핵심에 해당하는 에러 로그입니다.

     

    [그림 5] 에러 로그

     

    에러 로그의 내용은 Jupiter가 해당 파라미터에 등록된 ParameterResolver가 없어서 발생한 에러라는 것을 의미하고 있었습니다. 이 문제를 해결하기 위해서는 Jupiter라는게 정확히 무엇인지부터 알아야 했습니다. 현재 저희가 사용하고 있는 JUnit은 5버전인데, 이 JUnit 5가 어떤 구조로 이루어져 있는지를 당시의 저는 몰랐었습니다.그래서 문제를 해결하기에 앞서 JUnit 5의 구조에 대해서 공부해야할 필요성 또한 느꼈습니다.JUnit 5는 어떻게 구성되어 있는지, Jupiter의 역할은 무엇인지 알아야 이 문제의 해결에 실마리를 잡을 수 있을 것 같았습니다.

     

    JUnit 5 Architecture

    JUnit 5의 Architecture는 크게 세 부분으로 나누어져 있습니다.

    [그림 6] JUnit 5 Architecture

     

    각각 하나씩 간단히 알아보겠습니다. 먼저, JUnit Platform은 테스트를 실행할 수 있는 Test Engine을 포함하며 여러 Tool(콘솔, eclipse, Intellij…)에 일관성 있는 API를 제공하는 역할을 담당합니다. Jupiter와 Vintage 모두 이 JUnit Platform의 Test Engine을 구현한 구현체입니다. 이 Jupiter와 Vintage의 차이점은 Jupiter의 경우 우리가 사용하는 JUnit 5의 구현체이며, Vintage의 경우 하위 JUnit 버전(JUnit 4, JUnit 3)를 지원하는 구현체입니다. 그래서 우리는 일반적으로 JUnit 5로 테스트 코드를 작성한다고 하면 Jupiter를 사용한다고 이해하면 되겠습니다. 여담으로 JUnit 팀이 이러한 JUnit Platform의 Test Engine을 구현하게 하는 이유를 다음과 같이 말하고 있습니다.

     

    [그림 7] JUnit Platform의 Test Engine 구현 이유  (출처 : nipafx)

     

     

    Kakao i 번역기를 돌리면 다음과 같이 번역이 됩니다.

     

    [그림 8] Kakao i 번역기 화면 예시

     

    JUnit Platform과 구현체를 분리한 이유는 다른 테스트 프레임워크들이 Test Engine API를 구현하게 함으로써 JUnit의 생태계를 넓히고 나아가 도구들(e.g. IDE)과의 일관성 또한 가지게 하기 위함이었습니다.

     

     

    에러의 원인

    그러면 Jupiter는 왜 ParameterResolver를 못찾는 것일까요? Spring에서의 생성자 주입의 경우 메인 코드에서 이를 담당하는 것은 Spring IoC Container 입니다. Spring Framework의 Spring IoC Container는 Bean들을 관리하고 있다가 필요한 시점에 Bean을 주입해주는 역할을 담당합니다. 메인 코드에서 @Autowired construct를 굳이 명시해주지 않아도 Bean 주입이 가능했던 이유는 Spring IoC Container가 적절한 시점에 생성자 주입을 해왔기 때문입니다. 그러나 테스트 코드의 경우는 조금 다릅니다. 테스트 프레임워크에서 테스트 클래스의 관리 주체는 Spring IoC Container가 아닌 Jupiter가 담당합니다. @Autowired construct를 명시적으로 테스트 클래스 생성자에 알려주어야지만 Jupiter가 Spring IoC Container에게 Bean 주입을 요청할 수 있게 됩니다. 테스트 프레임워크에서의 주체는 Jupiter이므로 아무리 생성자 주입이라고 하더라도 @Autowired construct 애노테이션이 명시되어 있지 않은 테스트 클래스에 Bean 주입을 받을 수 없게 됩니다. 결과적으로 Jupiter는 자기 딴에는 열심히 생성자 매개 변수를 처리할 ParameterResolver를 찾아보려 노력하지만 없었고, 결과적으로 예외를 뱉게 되었습니다.

     

    [그림 9] Jupiter가 애노테이션을 확인 후 Spring에게 빈 주입 위임

     

    [그림 10] 메인 코드에서 생성자 주입 담당

     

    [그림 11] 테스트 코드에서 생성자 매개 변수를 처리할 Parameter Resolver를 탐색

     

    [그림 12] 테스트 코드에서 적절한 Parameter Resolver의 탐색 실패 후 예외 처리

     

     


    마치며

    프레임워크의 사전적 의미는 뼈대라는 뜻이 있지만, 소프트웨어에서의 프레임워크의 의미는 코드를 컨트롤하는 주체가 사용자가 아닌 프레임워크에 있다는 의미가 있습니다. 제가 이 문제가 발생한 원인 또한 테스트 프레임워크에서의 코드를 컨트롤하는 주체가 누구인지를 몰랐기 때문에 발생한 문제였습니다. 이번 기회로 많은 것들을 배울 수 있었는데 혼자 알기보다는 공유해서 더 많은 분들께 도움이 되었으면 하는 바람에서 이 글을 작성해보았습니다. 부족한 글이지만 도움이 되셨기를 바라며 글을 마칩니다. 끝으로 문제 해결에 도움을 주신 PJ에게 진심으로 다시 한번 감사드립니다.

     

     

    새로운 길에 도전하는 최고의 Krew들과 함께 해요!

    [대화형서비스플랫폼] 서버 개발자 모집

    harry.code (조민국)

    아직은 모르는게 많은 신입 개발자입니다.

    하나를 공부해도 깊게 공부하고 이해하려고 노력하고 있으며 새로운 것을 배우는 것 또한 좋아합니다.

     

    저작자표시 비영리 변경금지

    Tag

    DI, junit5, Jupiter, Kakao Enterprise, Kotlin, Spring Boot, 주니어개발자, 카카오엔터프라이즈, 카카오엔터프라이즈 기술블로그, 카카오엔터프라이즈 영입

    관련글

    • 카카오엔터프라이즈의 ‘조금 남다른’ AI 교육 이야기
    • [KREW INSIDE] AI 서비스의 기술 문서를 책임지는 사람들, 테크니컬 라이터
    • [KREW INSIDE] 현직 개발자가 들려주는 카카오엔터프라이즈
    • [TW] 기술 문서 작성 5단계

    댓글0

HOME
  • Tech Log
  • Krew Insight
  • Tech Trend
  • Tech & News
  • About Us
  • 페이스북
  • 유튜브
  • 인스타그램

If Kakao Enterprise, ( ㅤ )

기술과 서비스,
그리고 혁신적인 아이디어로 우리 모두의 꿈을 현실로 만듭니다.

바로가기
카카오엔터프라이즈 인재영입

경기도 성남시 분당구 판교역로 235 에이치스퀘어 N동 (주)카카오엔터프라이즈

Copyright © Kakao Enterprise Corp. All rights reserved.

  • 페이스북
  • 유튜브
  • 인스타그램
관련사이트
  • 카카오엔터프라이즈
  • 헤이카카오
  • 테크 그라운드
  • 기술문서

티스토리툴바