[우아한테크코스 6기] 레벨 3 마무리: 레벨 인터뷰 회고

, ,

🤨 레벨 인터뷰?

레벨 3이 마무리되고 학습 돌아보기 레벨 인터뷰가 진행된다는 공지가 있었다. 7-8명이 함께 한 조가 돼 인터뷰어/인터뷰이/옵저버로 나뉘어 서로 학습을 돌아본다. 사전에 인터뷰를 위한 자료를 제공해야 했었는데, 레벨 3을 진행하면서 내가 맡은 일을 위주로 작성해나갔다. 크게 학습과 협업의 측면에서 자료를 작성해야 했고, 나는 아래와 같이 작성했다.


개발 측면

도메인 구조 논의, 좋은 코드에 대한 고민

레벨 1, 2를 거치면서 객체지향이 곧 좋은 코드라고 생각했었지만, 그 반대로 좋은 코드를 위해 객체지향이 사용된다는 것을 알게 되었어요.

JUnit Extension을 활용한 테스트 격리

이전까지는 테스트 케이스의 격리를 위해 @DirtiesContext 을 사용했어요. 매번 스프링 컨텍스트가 초기화되어 느렸던 테스트 속도를 JUnit의 Extension을 활용해 향상했습니다.

로깅, 모니터링 도입

서버 에러를 찾기 위해서 부담스러운 과정을 거치는 대신, 로깅 및 모니터링을 통해 손쉽게 예외 사항을 확인할 수 있게 되었습니다.

API First Design 도입 시도, 대안으로 기존 Swagger를 RestDocs로 전환

코드를 구현하기 전, 프론트와 백엔드 사이의 약속인 API 문서를 먼저 작성하기 위해 노력했어요.

Random, ThreadLocalRandom과 SecureRandom의 장단점 학습 및 기술 선택

어떤 기술을 골라야 하는지에 대한 생각을 넓힐 수 있었습니다. 지금 문제 상황을 인식하고, 해결할 수 있는 기술을 비교해가며 선택할 수 있었습니다.

개발 서버 HTTPS 적용

평문으로 전달하던 요청/응답 내용을 암호화해 보안을 강화했습니다. 추후 운영 서버에서도 암호화를 적용해야 하므로, 설정이 간편한 Docker를 활용했습니다.


협업 측면

체크인 점수

매일 데일리 미팅에서 크루들의 상태를 파악합니다. 팀원과 일상적인 이야기를 나누는 것으로 하루를 시작했어요.

프론트엔드, 백엔드의 협업을 위한 상호 페어 프로그래밍 진행

프론트엔드가 백엔드 코드를, 그 반대도 경험하면서 서로의 업무 방식을 이해했어요.

CI/CD 파이프라인 학습 및 팀원들에게 학습 내용 공유

분야와 상관없이 CI, CD에 대한 학습을 도왔습니다. 이를 기반으로 지속적인 테스트와 통합을 이룰 수 있었어요.

합의된 화면 정의서로부터 출발하기

API 문서를 정하기 전에, 사용자가 어떤 화면을 마주할지 기획합니다. 백엔드 팀원들은 어떤 방식으로 서비스가 표현될 수 있을지 머릿속에 그려볼 수 있고, 프론트엔드 팀원들도 추가적인 부담 없이 정의서에 맞게 구현해낼 수 있어요.

A4 한장이라 생각보다 많이 적어야할 것 같았는데, 막상 적으니 효율적으로 줄여나가는 게 더 중요해졌다. 이력서를 쓸 때 이런 느낌일까? 쓸 이야기는 많은데, 정말 중요한 것만을 잘 써내려가는 게 어려웠다.

사실 가볍게 인터뷰를 하는 줄 알았다. 인터뷰는 수요일, 목요일 이틀로 나눠서 진행되는데, 나는 목요일이라 수요일에 다른 크루들이 인터뷰를 진행하는 걸 봤다. 🔥 생각보다 면접 분위기가 강해서 놀랐다. 크루는 물론이고 코치분들까지 함께 인터뷰를 진행하는 걸 보고 제대로구나.. 싶었다.

🎤 인터뷰 진행

우리 조는 리브, 레모네, 로빈, 산초, 도비와 나였고, 코치는 포비였다. 원래 네오가 코치로 있는 팀이 무슨 일인지 뿔뿔이 흩어져서 백호가 나중에 합류하게 되었다. 맨 마지막 순서라 꽤나 긴장을 많이 했는데, 백호에게 부탁해서 어떤 질문이 오고갔는지를 자세하게 기록할 수 있었다 🥹

아래부터는 인터뷰를 재구성한 내용이다. 기억이 흐려져 다소 정확하지 않을 수 있으나, 전체적인 흐름이나 질문을 참고하면 좋겠다. 편의상 평어로 기록한다. 주어진 인터뷰 시간은 20분이었다.

Q. 레벨 3때 진행한 프로젝트를 설명해 달라.
우리 서비스는 리뷰미라고 한다. 개발자가 나를 아는 것이 중요한데, 나로부터 나를 알아가는 것이 아니라 팀원으로부터 강점을 알도록 하는 서비스다.

Q. 좋은 코드를 위해 객체지향이 쓰인다는 것을 느끼게 된 계기를 설명해 달라.
레벨 1, 2에서는 자바를 활용한 객체지향을 많이 배웠다. 그 과정에서 ‘객체지향이 곧 좋은 코드’라는 인식이 자리잡았다. 레벨 3에서 팀 프로젝트를 진행할 때, 객체지향을 완벽하게 지켜가면서 DB까지 들여올 수 없었다. JPA를 활용할 때 많은 양방향 의존관계로 인해 어려움을 겪었다. 결국 생명주기가 완벽하게 일치하는 클래스끼리만 연관관계를 활용하고, 그렇지 않으면 모두 끊어 주었다. 절차지향적인 부분도 많았지만, 변경에 유연했다. 이 과정에서 ‘객체지향이 곧 좋은 코드’가 아니라, ‘좋은 코드로 가는 길에 객체지향이 있다’는 것을 알게 되었다.

Q. API 문서를 Swagger에서 RestDocs로 옮겼는데, 이유가 있는가?
API 문서는 프론트엔드, 백엔드 모두가 개발에 들어가기 전에 만들어져야 한다고 생각한다. 하지만 Swagger를 활용하면 백엔드의 API가 완성된 이후에 프론트엔드가 개발에 들어가게 되거나, 추가적인 노션과 같은 문서를 활용해야 한다. 한 곳에서 문서를 관리하고 싶었기에 이와 관련한 내용을 찾아보았다. OpenAPI를 활용하면 yml 문서를 기반으로 함께 출발할 수 있지만 러닝커브를 고려해 도입하지 않게 되었다. RestDocs에서 서비스 레이어를 Mocking한다면 API 문서를 생성할 수 있기에, 이후부터는 이와 같이 API 문서를 관리하고자 한다.

Q. 인터넷 주소창에 https://review-me.page 를 접속하면 일어나는 일을 최대한 자세하게 설명해 달라.
(뒤쪽의 화이트보드를 활용해 그림을 그려 가면서 설명)
우선 도메인 이름을 IP로 변환해야 하기 때문에, DNS 서버를 통해 해당 도메인의 IP를 알아낸다. IP를 알아내고 나서 라우터를 거쳐 서버로 요청이 들어온다. nginx가 응답을 받고, 톰캣 서버의 8080 포트로 변환해 요청을 전달한다. 스프링 프레임워크의 Dispatcher Servlet은 HandlerMapping을 통해 컨트롤러에서 등록한 적절한 메서드를 찾고, 로직을 수행하도록 한다. 만일 적절한 메서드가 없는 경우 404 예외를 발생한다.

Q. 정적 페이지도 API 서버에 요청하는가?
아니다. 잘못 이야기했다. 정적 페이지의 경우 CloudFront에서 제공하고 있으며, 이때는 API 서버를 거치지 않는다. (그림을 다시 수정해서 그림)

Q. 톰캣의 Dispatcher Servlet에서 Controller를 거쳐 DB까지 하나의 로직이 수행되는 과정을 설명해 달라.
Request가 DispatcherServlet에 도달하면 HandlerMapping의 과정을 수행한다. 스프링 어플리케이션이 처음 작동할 때 @Controller 어노테이션을 포함하는 클래스를 빈으로 등록하고, 그중 @XXXMapping에 해당하는 것을 찾아 메서드를 실행한다. Service 레이어에서 Repository를 통해 데이터를 받아오며, 웹 응답 객체를 생성한다. 웹 응답 객체는 Http 형식에 맞게 변환돼 다시 클라이언트에게 돌아온다.

Q. 레이어드 아키텍처를 사용했을 텐데, 비즈니스 로직이 어느 레이어에 위치하는가?
처음 설계할 때에는 리뷰라는 도메인에서 길이와 같은 사항을 검증했는데, 도메인에 모든 검증이 존재하면 확장하는 데에 어려움을 겪게 된다. 가령 글자 수 제한이 더 늘어난 롤링 페이퍼를 도입한다면, 현재 도메인을 재사용할 수 없다. 따라서 Service 레이어에서 검증을 수행하도록 했다. 도메인을 최대한 방어적으로 사용하고 있다.

Q. 로깅, 모니터링에 어떤 기술을 활용했는가? 그 이유는 무엇인가?
Spring Actuator, Loki, Prometheus와 Grafana를 사용하고 있다. 팀원 모두 로깅, 모니터링에 처음 접하는 상태라 ELK와 앞서 이야기한 기술 중 고르게 되었다.

Q. 로깅에서 누가/언제/무엇을 했는지를 기록하고 있는가?
현재는 아직 진행하고 있지 않다. 아직 완전히 로깅을 어떻게 할지 자리잡히지 않았기 때문에, 서버 내에서 일어나는 각종 예외 사항들에 대해서만 기록하고 있다.

Q. 앞으로는 할 계획인가?
개인적으로는 요청부터 서버 내 로직을 통한 응답을 모두 기록하고 싶다. 오류가 발생했을 때 어떤 이유인지를 확실하게 파악하기 위해서다. 하지만 팀원들과의 충분한 협의가 이루어져야 하기 때문에 회의를 통해 결정하게 될 것이다.

(답변 시간 3분 남음)
Q. 이전 크루들이 답변하지 못한 것을 질문해 보겠다. 몇몇 언어는 싱글 스레드 환경에서 많은 트래픽이 들어올 때 이를 감당하고 처리할 수 있다고 한다. 어떻게 이런 방식이 가능할까?
(생각할 시간을 부탁함)
멱등성이 보장되는 GET, PUT과 같은 요청을 한 번에 받도록 하고, 그렇지 않는 것은 따로 생각해봐야 할 것 같다.

Q. 멱등성이 보장된다고 하더라도 DB를 확인하기 위해서는 시간이 오래 걸릴 듯하다.
(생각할 시간을 부탁함)
추측하건대, DB에 요청한 뒤 이를 기다리면 많은 트래픽을 감당할 수 없다. 이벤트 발행과 같은 형식으로 DB에 요청한 뒤, 완료되었을 경우 값을 가져오는 형태로 진행하면 될 것 같다. 주기적으로 요청이 준비되었는지 물어보는 방식이 하나의 수단이 될 수 있겠다.
(지금 생각해보니 Non-blocking IO를 의도하고 질문한 듯하다. 알고 있는 용어였는데 빙빙 돌려 말하게 되어서 아쉬웠다)

🤯 이럴 줄은 몰랐어요

가볍게 회고를 할 줄 알았으나.. 완전 면접 분위기였고 내가 제대로 된 답변을 했는지도 모르겠다 ㅋㅋㅋ 몇몇 부분은 올바르지 않을 수 있으니 감안하고 읽어주었으면 좋겠다 ㅎㅎ.. 내가 말하면서도 어떤 부분을 확실히 알고 있는지, 어떤 부분이 미흡한지를 확 와닿게 돼서 정말 유익한 시간이었다. 그것과는 별개로 에너지가 정말 많이 소모되기는 했다.

좋은 피드백도 많이 받았다. 확실히 경험에서 우러나오는 이야기가 다른 사람들의 공감을 일으키는 듯했다. 나의 신념(?)을 뒤흔들었던 객체지향과 좋은 코드에서의 고민은 정말 소중한 경험이었다. 다른 크루들이 좋게 바 줘서 고마웠고, 특히 포비가 좋게 봐주셔서 감동이었다 🥹

이전에는 레벨 2에서부터 레벨로그라는 이름으로 인터뷰를 진행했다고 하는데, 6기부터는 레벨 3에야 인터뷰가 들어와서 살짝 아쉬웠다. 일찍부터 이런 내용을 했더라면 더 나를 알아갈 수 있지 않았을까? 싶기도 하지만… 팀 프로젝트를 겪으면서 이런저런 고민을 많이 해보지 않고 인터뷰를 하는 건 또 속이 비어있는 느낌이 될 듯하다. 좋은 타이밍에 좋은 크루들과 좋은 경험을 했다! 다음 레벨에서는 어떤 내용을 더 보강해야 할까? 🤨

Categories