ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2023년 5월 1주차 주간회고
    주간 회고 2023. 5. 10. 18:20

     

     

    과제 테스트 끝.

    어제 부로 과제가 끝났다. 정규 기간은 1주일이었지만, 더 잘 해보고 싶다는 생각에 6일 연장을 요청드렸고, 요청을 받아주셔서 거의 2주 동안 과제를 진행했다. 제출하고 나서는 무슨 수능이라도 끝낸 것마냥 공허함이 몰려왔고, 밤동네를 몇 시간이고 걸어다녔던 것 같다.
     
    원래 같았으면 다른 프로젝트를 진행하면서 적용을 시도했을 것들을 이번 과제를 진행하면서 적용해보게 되었다. 그 과정에서 적용했으나 정리하지 못했던 몇 가지 개념들의 사용 후기와, 과제를 하면서 아쉬웠던 점들을 정리해보고자 한다.


    빌더 패턴

     
    예전에 개인 프로젝트를 진행할 때 저렇게 쓰면 안 된다고 혼났었던 잘못된 fake 메서드, 아니 정적 팩터리 메서드 사용 예시. 지금 보니까 이제는 정말로 저 Long 타입의 인자들이 각각 무엇을 의미하는 지 모르게 되었다.
     
    동일한 문제를 반복할 수는 없다고 생각했다. 과제를 시작하기 얼마 전에 마침 빌더 패턴에 대해 알게 되었었는데, 이것으로 문제 해결을 시도할 수 있을까 싶어 과제에 해당 방식을 도입해보았다.
     
    빌더 패턴을 간단하게 이야기하자면 객체 내부에 static 클래스로 Builder 객체를 놓고, 객체를 생성할 때 Builder에 정의되어 있는 객체의 필드 값을 받아 Builder 자신을 반환하는 메서드를 메서드 체이닝 형태로 호출해 Builder에 인자 값들을 저장해놓은 뒤, 최종적으로 .build() 메서드에서 대상 객체를 생성해 반환하는 객체 생성 방식이다.
     

     
    과제에서는 객체를 이런 식으로 생성했다. 여기서는 한창 혼나던 시절에 받았었던 피드백 한 가지도 추가로 반영했는데, 해당 Entity가 다른 Entity의 id 값을 갖고 있어야 하는 경우 단순히 Long 타입 값을 전달하는 것이 아닌, 해당 Entity 객체를 직접 전달해 객체 내부에서 해당 객체의 id를 꺼내 전달하도록 했다.
     
     
    Builder 패턴을 사용하면서 기존에 정적 팩터리 메서드를 잘못 사용하면서 겪었던 문제들에 대해서는 확실하게 개선점을 가져갈 수 있었던 것 같았다.
     
    - 우선 인자에 전달하는 것들이 무엇인지 확실히 드러낼 수 있게 되었고,
    - 테스트 코드에서 객체를 생성할 때, 다른 Entity와 연관관계를 갖는 상위 인자의 식별자가 수정되었을 때, 해당 인자의 식별자만 수정해도 다른 객체들의 인자를 일일이 찾아다니면서 수정하지 않아도 되게 되었다. 위의 사진을 예시로 들자면 course 인스턴스의 id 값이 다른 값으로 바뀌었다고 할 때, lecture1of1과 lecture1of2의 courseId를 일일이 찾아다니면서 일치시켜주지 않아도 되게 되었다.
     
     
    다만 여전히 고민이었던 부분도 있었는데,
     
    - 다른 Entity와 연관관계를 많이 갖는 Entity 객체를 생성할 때에는 필요한 Entity 전부를 준비해야 하는 문제는 여전히 존재했다. 예를 들어 수강신청서라는 Entity가 있고, 이 Entity는 학기, 학생, 강의, 과목 정보를 갖는다고 하겠다. 그렇다면 하나의 수강신청서 객체를 만들려면 학기, 학생, 강의, 과목 객체도 실제로 있어야 한다는 것이다. 실제로 수강신청서 Entity를 다양하게 준비한 뒤 발생할 수 있는 케이스를 테스트하려 했을 때, 필요한 다른 객체들의 인스턴스들이 산더미같이 생겨났다. 어떤 테스트 클래스에 200줄의 코드가 있다고 하면 한 150줄 정도는 모의 객체들을 준비하는 코드일 정도였으니. '객체에 따라서는 실제로 그렇게 많은 setUp을 해야 할 수도 있겠지!'와 '근본적으로 연관관계에 대한 설계를 다시 생각해봐야 하는 거 아냐?'라는 생각이 서로 충돌했고, 일단은 전자의 생각을 따랐다.
     
     

    Filter

    'Intercepter가 있는 것을 봤는데, Filter 대신에 Interceptor를 사용한 이유는 무엇이었나요?'
     
    취업준비를 하던 초기, 면접장에 갔었을 때 받았던 질문. 당시에는 Filter가 뭔지 아예 몰라 잘 모르겠다고 대답할 수밖에 없었다. 최근 다른 커피챗에서 나눴던 이야기들 중 Filter에 대해 한 가지 들었던 내용으로 '특정 요청에 대해서만 처리할 것인지, 모든 요청에 대해 처리할 것인지'에 따라 쓰는 경우가 달라질 수 있다는 언질을 들었던 기억이 떠올랐다. 마침 과제에서 주어진 요구사항들을 분석했을 때, 모든 요청에 대한 인가가 필요하다고 느껴져 Filter를 적용해보았다.
     
    그런데 Filter를 적용해보면서 예상과는 다르게 뭔가 이상한 점이 느껴졌는데, 생각해보니 프로젝트에서 Interceptor를 적용했었을 때도 모든 요청은 Interceptor를 거쳤다는 점이었다. 코드 관점에서는 Filter를 이용해 인가를 적용하는 방식이나, Interceptor를 이용해 인가를 적용하는 방식이 사실상 똑같았다. 차이가 있다면 Filter에서는 ServletRequest, ServletResponse를 사용하고, Interceptor에서는 이들을 상속받은 HttpServletRequest, HttpServletResponse를 사용한다는 정도. 그래서 일단은 HttpServletRequest에 대한 작업 처리가 필요했기 때문에 ServletRequest를 HttpServletRequest로 타입을 캐스팅해 사용한 상태였다.
     
    의구심을 해결하고자 다시 한 번 둘의 차이에 대해 찾아보았고, 둘의 사용을 가르는 기준은 Spring 컨텍스트 바깥에서 공통적으로 처리해야 하는 작업에 초점을 맞출 것인가, Spring 컨텍스트 내에서 API 요청과 Controller로 넘겨주기 전에 수행할 작업에 초점을 맞출 것인가에 중점을 두어 사용할 것인지 판단한다는 것을 확인할 수 있었다. 나의 경우에는 API 요청 시 요청을 보낸 사용자를 식별해 인가하기 위한 목적이었으니, Interceptor에서 수행하는 게 좀 더 어울리는 동작을 굳이 Filter에서 수행하도록 했다는 느낌.
     
    지금 생각해보면 다소 아쉬웠던 기술 적용이었다고 할 수 있겠다. 어떤 기술을 도입한다고 하면, 그 기술이 구체적으로 어떤 문제를 해결하는 것을 타겟으로 두고 있는지를 조금이라도 살펴본 뒤에 도입을 검토할 수 있어야겠다.
     

    Reference

    - https://algopoolja.tistory.com/110
    - https://www.baeldung.com/spring-mvc-handlerinterceptor-vs-filter

     

    Nested Test

    개인 프로젝트를 하던 때 프론트엔드 애플리케이션에서 사용했던 Describe-Context-It 구조를 프로젝트에 도입해보았다. 테스트의 구분은 다음과 같이 시도했다.
     
    어떤 .class 파일에 작성된 코드를 테스트한다고 할 때,

    - Describe: 테스트하려는 메서드를 지정했다. @Nested 어노테이션으로 테스트 클래스 내부에 별도의 클래스를 정의했다.
    - Context: 메서드에 주어지는 인자의 상태 등을 지정했다. @Nested 어노테이션으로 Describe에 대한 클래스 내에 다시 한 번 별도의 클래스를 정의했다.
    - It: 메서드를 수행했을 때의 결과를 분석했다. Context에 대한 클래스 내에서 @Test 어노테이션으로 테스트 메서드임을 나타냈다.
     
     
    Nested Test를 도입했을 때 가장 크게 느꼈던 이점은 각 테스트에 맞는 데이터만을 좀 더 명시적으로 구분해 setUp할 수 있게 되었다는 것이다. 같은 클래스 안에 있더라도 각 메서드에서 사용하는 인자들은 서로 다를 수 있다. 만약 모든 메서드에 대한 테스트를 동일한 클래스 depth에서 수행하려 한다면, 테스트를 수행하는 데 필요한 인자들을 '모두 한 번에' setUp해야 한다.
     
    예를 들어 수강신청 관리자라는 객체가 있다고 치고, 다음의 두 가지 기능을 테스트해야 한다고 해 보자.

    - 특정 강의가 특정 학기에 제공되는지 확인하는 method
    - 특정 학생의 수강신청서 목록에 특정 과목에 대한 수강신청서가 존재하는지 확인하는 method

     
    모든 메서드의 모든 테스트 케이스를 동일한 클래스 depth에서 테스트하는 경우, @BeforeEach 어노테이션을 부여한 메서드에서 다음의 데이터들을 모두 한 번에 setUp해야 한다.

    - 과목
    - 학기
    - 강의
    - 수강신청서 목록
    - 수강신청 관리자
    - 그리고 이들을 생성하기 위해 필요한 다른 모든 Entity들

     
    이렇게 되면 각 메서드를 테스트하는 데 필요한 각각의 데이터 양은 적을 수 있어도, 한 번에 setUp하는 과정에서는 덩어리들이 많아져 분간하기 어려울 수 있고, 그 덩어리의 데이터 안에서 특정한 데이터가 어떤 메서드를 테스트하는 데 사용될 것인지를 짐작하기가 쉽지 않다.
     
    반면 테스트 코드가 구조화되면, 다음과 같이 setUp해야 하는 데이터가 구분된다.

    - 특정 강의가 특정 학기에 제공되는지 확인하는 method
      - 강의
      - 학기
      - 수강신청 관리자 및 필요한 다른 Entity들
    - 특정 학생의 수강신청서 목록에 특정 과목에 대한 수강신청서가 존재하는지 확인하는 method
      - 수강신청서 목록
      - 과목
      - 수강신청 관리자 및 필요한 다른 Entity들

     
    각 메서드 안에서 setUp되어야 하는 필요한 데이터가 무엇인지 확실하게 구분할 수 있게 되었다는 점이 Nested Test 구조를 도입하면서 가장 좋았던 부분인 것 같다.
     
     

    한 부분에 너무 매몰되기보다는 전체적인 흐름을 바라볼 수 있어야 한다.

    요구사항을 분석하고 난 뒤, 과제를 마치기 위해서 정복할 수 있어야 한다고 생각한 영역들은 다음과 같았다.
     
    - 객체를 설계하는 영역
    - API를 설계하고, 동작에 맞게끔 레이어를 작성하는 영역
    - 코어 로직을 구현하는 영역
    - 신기술을 학습하고 적용하는 영역


    과제를 하면서 아쉬웠던 점이라 한다면, 역시 시간 분배에 대한 아쉬움을 빼놓을 수 없을 것 같다. 시간 연장을 부탁드렸던 것은 위의 영역들 중 객체 설계와 신기술 학습과 적용에 많은 시간이 걸릴 것을 염두에 둔 것이었다. 그러다보니 코어 로직은 객체를 잘 설계했으면 며칠 정도만 들여도 금방 구현할 수 있지 않을까 생각해 시간을 많이 분배하지 않았다.
     
    그래서 나머지 세 단계에 시간을 상당히 많이 들였다. 점진적으로 객체 설계를 확장해나갔고, API 요청에 대한 동작을 구현할 때 각 기능 하나에도 각 레이어에서 검증할 수 있는 예외 케이스를 최대한 고려하려고 했고, 신기술을 도입하는 과정을 기록으로 남기는 데에도 꽤 많은 시간을 들이기도 했다. 문제는 코어 로직을 구현하는 시점이 되자 예상했던 것보다 시간이 훨씬 많이 남아있지 않았고, 마주한 구현 난이도도 예상했던 수준보다 상당히 높았다. 구체적으로는 내가 구상한 방식을 따를 때 코드의 중복을 최소화할 수 있는 방안을 마련할 수 있어야 했는데 이를 고려할 시간이 부족했고, 결국 코드의 중복이 상당 부분 존재하는 상태로 코어 로직을 구현하게 되었다.
     
    내가 수행하고 있는 것이 하나를 깊게 수행할 수 있는 '학습'이 아니라 전체적인 흐름을 만들어내야 하는 '과제'였다는 것을 생각한다면, 이는 상당히 아쉬운 점이라고 할 수 있겠다. 이번 과제뿐만 아니라, 계속해서 취업을 준비하고 있는 상황에서도 초점을 두고 주목하고 있는 한 부분에 너무 매몰되어 전체적인 흐름을 놓쳐버리고 마는 좋지 못한 케이스를 반복해서는 안 될 것이라고 느껴진다.
     
     
     
     

    '주간 회고' 카테고리의 다른 글

    2023년 5월 3주차 주간회고  (1) 2023.05.22
    2023년 5월 2주차 주간회고  (0) 2023.05.14
    2023년 4월 4주차 주간회고  (0) 2023.05.01
    2023년 1분기 회고  (2) 2023.04.22
    2022년 회고  (8) 2022.12.31

    댓글

Designed by Tistory.