240517 Spring 숙련 - 과제 1차 제출하고 발전시키기

노재원·2024년 5월 17일
0

내일배움캠프

목록 보기
41/90

프로젝트 단위의 Readme 작성하기

1차 제출의 날이 왔는데 뭔가 서버 앱은 과제의 용도로 제출할 땐 어떤 정보를 표기해야하나 여러모로 고민이 됐다.

이전 과제는 사실 계층으로 나뉠 것도 없었고 코드 견본만 잘 넣어놨어도 괜찮은 수준의 Kotlin 앱일 뿐이었는데 이번엔 본격적으로 계층이 구분되었고 관심사와 역할이 많아진 서버 앱이니만큼 모든 내용을 넣을 순 없었고 파악은 쉽게 가능하게 해야했다.

허구한날 읽던 Readme는 라이브러리 단위 Readme밖에 없었는데 막상 이렇게 작성하다보면 어렵긴 한 것 같다.

고민 끝에 정한 목차는 다음과 같았다.

  • 📚 API 명세서
  • ☁️ ERD Cloud
  • 📦 패키지 구조
  • 🔄 통신 흐름
  • ⚙️ 주요 기능
  • 💻 개발 환경

Swagger를 명세서로 삼는건 한 눈에 여러 API를 담기엔 가독성이 좋지 않아서 Notion 페이지로 작성한 걸 그대로 꽂았고 나머지는 그냥 내용을 욱여넣는 방식이긴 하다.

패키지 구조는 계층별로 나뉘어서 파악하기 힘든 구조를 정리했고 /api/v1으로 이동할 수 있게 해놨다.

통신 흐름은 그냥 DDD 아키텍처 흐름을 ASCII 아트로 넣어놨다. 최대한 지키려고 생각하며 짰으니 실제로도 문제는 없을 것이다.

주요 기능에는 그래도 견본조차 없으면 파악하기 힘들 것 같아 짧게 견본으로 각 Layer의 코드를 하나씩 빼서 넣어놨다.

튜터님이든 면접관이든 남 보여주려고 하는 용도니 귀찮아도 이게 뭐지 보단 그런 느낌 정도의 생각이라도 들 수 있게 신경썼다.

Entity > Dto 변환 Util 만들기

Todo와 ManyToOne 관계의 Comment는 단일 Todo를 조회했을 때 양방향이 아니기에 Comment는 따로 조회를 해야 했다.

문제는 TodoDto 에 comments를 추가하면서 생겼는데 Todo Entity는 Comment 정보를 아예 모르기 때문에 Entity에서 Dto 변환을 분리하고 Service layer에서 생성해야 했다.

관련해서 레퍼런스를 찾아보니 애초에 Entity 에서 Dto 변환을 하는 것은 괜찮은가? 또는 Entity를 Dto로 변환하는 건 어느 계층에 속하는가? 라는 주제로도 질문이 몇 개 있었고 그에 대한 해답도 여러가지 있었다.

Entity <-> Dto 의 해석 자체는 은근 다양한 면이 있어서 양방향 맵핑을 즐겨 사용하는 사람은 Entity 에서 Dto 변환을 진행한다고 하고 아닌 경우에는 Service, Controller 의견이 분분했는데 나는 그 중에서도 계층을 따지자면 Service에서 주로 사용되긴 하지만 Dto 변환 자체가 Service 계층에 속할 필요는 없다고 생각이 들었다.

그래서 최종 선택한 부분은 domain package 밖으로 빼서 util package를 만들고 그 안에 object DtoConverter 를 만들어 DtoConverter.convertToxxxDto 같은 방식으로 관리하기로 했다.

Service 계층에서만 사용할 것 같긴 한데 service package에 일일이 TodoDtoConverter 같은 걸 만들어 관리할 수는 없는 노릇인 것 같아 이번엔 이렇게 작성했는데 아마 이럴거면 Domain별로 관리하는게 좋다 라는 피드백이 들어올 수 있을 것 같긴 하다.

util 패키지에 이거 말고도 유틸, 헬퍼 클래스가 더 생길 여지는 있겠지만 DtoConverter 라는 object의 용도는 너무 Service 계층에서만 노는 느낌이라 정확한 정답은 모르겠다.

Entity-Dto Enum 검증에 대한 문제

이번엔 지난 번처럼 단순하게 Enum을 공유하겠다는게 아니라 검증의 접근에서 고민을 하게 됐다.

찾아보니 Jackson의 Custom validator를 쓰고 Entity-Dto Enum을 분리해서 작성하고 유효성 검증용으로만 쓰면 가능하다는 레퍼런스를 읽게 됐다.

이건 Entity의 Enum을 그대로 쓰기보단 한 번 걸쳐서 Body 유효성을 검증하니까 유효성 검증을 어노테이션 딸깍으로 커버하고 Entity의 Case와 분리가 되면서도 검증이 되는 그런 식으로 적용이 가능해보여 작성해보고 있었는데 여러 문제가 있었다.

우선 검증을 돕는 Custom Validator가 꽤 커졌고, Entity enum이 변경되면 즉시 전파되는 건 아니지만 어차피 검증 수정은 필요하고, Enum class만 달라질 뿐 체크 자체는 String으로 받던 것과 비슷한 느낌이라 튜터님께 가서 고민 상담을 해봤다.

여러가지 방면으로 해석을 도와주셨는데 내 머리로 열심히 이해해보니 이런 이슈가 생길 수 있었다.

  1. 클라이언트와 통신해야하는 Dto의 특성이 있어 클라이언트에서도 충분히 처리할 수 있는 부분이고
  2. 휴먼 에러 줄이겠다고 Enum 쓴 건데 협업하는 사람이 두 개의 Enum으로 더 힘들어할 수도 있고
  3. 실제 서비스도 유저에게 노출되는 에러 자체를 줄이는 안전지향을 선호하고
  4. 서비스가 커져 분리해야하는 상황에서 통신하기에도 유리하기 때문에

그리고 내가 클라이언트에서 API를 쓴다고 가정해도 이렇게 엄격해봤자 물음표밖에 뜨는 상황일 것 같은 것도 컸다. GraphQL 이면 이런 고민 다 해결해주긴 할텐데 이건 그냥 Rest API 아니겠는가?

그래서 그냥 Presentation layer에서 처리하는 Enum은 처음 의도대로 Entity와 관련없는 Query variable만 처리하기로 하고 Body는 String으로 받기로 했다.

지식이 늘긴 했으나 굳이 뻘짓을 한 것임은 부정할 수가 없겠다.


코드카타 - 프로그래머스 기사단원의 무기

숫자나라 기사단의 각 기사에게는 1번부터 number까지 번호가 지정되어 있습니다. 기사들은 무기점에서 무기를 구매하려고 합니다.

각 기사는 자신의 기사 번호의 약수 개수에 해당하는 공격력을 가진 무기를 구매하려 합니다. 단, 이웃나라와의 협약에 의해 공격력의 제한수치를 정하고, 제한수치보다 큰 공격력을 가진 무기를 구매해야 하는 기사는 협약기관에서 정한 공격력을 가지는 무기를 구매해야 합니다.

예를 들어, 15번으로 지정된 기사단원은 15의 약수가 1, 3, 5, 15로 4개 이므로, 공격력이 4인 무기를 구매합니다. 만약, 이웃나라와의 협약으로 정해진 공격력의 제한수치가 3이고 제한수치를 초과한 기사가 사용할 무기의 공격력이 2라면, 15번으로 지정된 기사단원은 무기점에서 공격력이 2인 무기를 구매합니다. 무기를 만들 때, 무기의 공격력 1당 1kg의 철이 필요합니다. 그래서 무기점에서 무기를 모두 만들기 위해 필요한 철의 무게를 미리 계산하려 합니다.

기사단원의 수를 나타내는 정수 number와 이웃나라와 협약으로 정해진 공격력의 제한수치를 나타내는 정수 limit와 제한수치를 초과한 기사가 사용할 무기의 공격력을 나타내는 정수 power가 주어졌을 때, 무기점의 주인이 무기를 모두 만들기 위해 필요한 철의 무게를 return 하는 solution 함수를 완성하시오.

문제 링크

fun solution(number: Int, limit: Int, power: Int): Int {
    var answer: Int = 0
    
    for (knight in 1..number) {
        val attack = knight.divisorCount()
        
        if (attack <= limit) answer += attack
        else answer += power
    }
    
    return answer
}
    
fun Int.divisorCount(): Int {
    var count = 0
    val sqrt = Math.sqrt(this.toDouble()).toInt()
    
    for (i in 1..sqrt) {
        if (this % i == 0) {
            count += if (i * i == this) 1 else 2
        }
    }
    
    return count
}

생각보다 쉬운 문제가 나와서 이제는 익숙해진 제곱근의 범위만큼으로 약수 구하는 걸로 짜서 제출했다. 대충 짧게 요약하면 제곱근의 범위만큼 순회해서 제곱수면 1, 다른 수면 2개의 약수가 존재한다는 공식이다.

처음엔 문제를 잘못 읽어서 제한을 초과한 기사는 세금의 개념으로 power짜리 무기를 추가구매 해야하는 줄 알고 power + attack 을 더했었는데 그냥 제한을 걸 뿐이었다.

개인적으론 더 줄일 방법도 생각도 안나고 해서 만족스럽고 Kotlin 스럽게 짜신 분들도 조금 계시긴 하던데 핵심 알고리즘은 똑같고 가독성도 그냥 사바사인 것 같아 넘기기로 했다.

0개의 댓글