우테코 1주차를 진행하며

박세건·2023년 10월 26일
0

처음에 접근했던 방식은 알고리즘 문제를 해결하듯이, 구현 기능 목록은 큰 기능들 위주로 간단하게 적고 무작정 결과만 나오게 해결하려고 했습니다.
리팩토링을 진행해 보려 했지만 이미 코드들이 서로 엉켜있어서 내가 작성한 코드의 의미를 내가 알아보지 못하는 경우가 발생했고 잘못된 방향으로 코딩을 진행했다는 것을 알게 되었습니다. 잘해보고자 했던 마음이 크고 급해져서 이러한 상황이 생긴 거 같았습니다. 문제를 해결한다는 방식이 아닌 코딩하는 방법을 배운다는 생각으로 요구사항 분석 및 구현 기능 목록 작성 부분으로 다시 시작하였습니다. 조금이라도 막혔던 부분은 바로 찾아보고 공부하면서 진행했습니다.

회고록

[요구사항 분석 및 구현 기능 목록 작성]

항상 정답만을 보고 해결하는 것에만 급급했던 저는 설계하는 부분에 대해서는 어색했습니다.
이전 우테코 선배님들은 이 부분을 어떻게 작성하였는지 알아보았고 이 방식을 토대로 저만의 규칙을 세웠습니다.
1. 요구사항과 문제의 실행 결과 예시를 확인한 뒤에, 프로그램에 필요한 큰 기능들을 항목화
2. 그 밑으로 위에 기능들을 완성하기 위해서는 어떤 작은 기능들이 필요한지를 항목화
3. 명확하게 구현 방식이 정해질 때까지 2번 과정 반복
이전처럼 머릿속으로 생각해서 목록을 작성할 때보다 이러한 규칙을 바탕으로 작성했을 때가 더 깔끔하게 정리해고 기능들이 명확해졌습니다.

[클린 코드의 중요성]

작성한 구현 기능 목록을 따라서 코딩을 작성하던 중 이전과는 다르게 보기 쉽고 깔끔하게 적어봐야겠다고 생각했습니다. 하지만 방법을 모르고 헤매던 저는 프리코스 커뮤니티의 함께-나누기 채널에서 클린 코드에 대한 정보를 얻을 수 있었습니다. 덕분에 저는 들여쓰기 규칙, 메서드가 한 가지 일만 담당하는지 등등의 클린 코드 규칙을 알게 되었고 이 규칙을 맞춰보면서 코드를 작성하였습니다. 규칙들에 맞춰서 작성하는 것이 처음이었기에 힘들었지만, 작성된 코드를 되돌아보았을 때 코드만 보고 해당 부분이 어떤 기능을 담당하는지 쉽게 알 수 있는 것을 확인했고 이것이 가독성을 높이는 부분이라는 것을 알게 되었습니다.

[메서드가 한 가지 일만 담당하도록 구현했는가]

특히 클린 코드 규칙 중에서 ‘메서드가 한 가지 일만 담당하도록 구현했는가?’ 이 부분을 지키기 위해 메서드를 작은 단위로 자르다 보니 자연스럽게 구현 기능 목록과 맞아떨어지게 구현할 수 있었습니다.
또한, 작은 단위의 공통적인 메서드를 갖고 있을 수 있다는 것을 발견했고 이를 ‘Util’ 클래스로 만들어서 관리했습니다. 1주 차를 진행하면서 많은 과정이 저의 성장을 도와주었지만, 특히 이 과정이 1주 차를 진행하면서 가장 의미 있던 과정이었습니다. 사소한 점이겠지만 이 사실을 알았을 때 나 자신이 너무나 대견했고 개발에 대한 더 깊은 흥미를 갖게 해주는 발견이었습니다.
많은 정보를 학습할 수 있는 커뮤니티를 열어주셔서 감사합니다!

[정답이 없는 문제의 매력]

조금 더 가독성을 높일 방법은 없는지, 클린 코드 규칙은 제대로 지켜졌는지, 기능에 맞게 네이밍이 잘 이루어졌는지, 또 다른 객체들을 설계할 수 있지는 않은지, 놓친 부분은 없는지 고민하였고 프로그램을 완성하는 시간보다 훨씬 더 많은 시간을 리팩토링하는 데에 사용했습니다. 하루도 빠짐없이 리팩토링하는 방법에 대해서 학습했지만 지금 작성된 코드가 옳은 코드인지에 대한 답이 없었기에 ‘정답이 없는 문제’라고, 생각했습니다. 결국 계속해서 더 나은 방법을 알기 위해 학습을 반복해야 했고 코딩하지 않을 때도 리팩토링할 방법에 대해 생각하게 되었고 빨리 리뷰하는 시간이 와서 많은 분의 의견 또한 듣고 싶어졌습니다. 또한, 이 프리코스 과정이 끝나고 성장하였을 때 진행했던 문제들에 대해서도 다시 한번 리팩토링해 보고 싶어졌습니다.
이러한 과정이 ‘정답이 없는 문제’가 가져다주는 효과적인 학습 방식이자 매력이라고 생각했습니다.

[클래스와 기능 분리]

이번 1주 차를 진행하면서 가장 많은 시간을 투자했던 부분입니다. 각각의 객체들에게 어떤 속성들을 쥐여줄 것인지, 세부적인 기능들이 어디 클래스에 지정되어야 하는지, 또 클래스는 어떤 패키지에 속해야 하는지가 고민이었습니다. 세부적인 기능들은 구현되었지만, 이 기능들이 어디 클래스에 위치해야 하고 어떻게 실행되어야 할지를 정하지 못해서 계속 코드를 수정하고 원래 대로로 돌리는 것을 반복했습니다. 많은 시간을 사용했지만 개선되지 않는 코드를 보는 것이 가장 힘들었던 시간이었습니다. 하지만 이 시간 또한 하나의 학습이자 성장하는 과정이라고 생각했습니다. ‘생활 코딩’ 유튜브의 객체 지향 프로그래밍이라는 영상을 보면서 객체 지향 프로그래밍에 대해서 다시 한번 공부하면서 리팩토링을 반복하다가 이전에 ‘김영한’님의 ‘스프링 핵심 원리’라는 강의를 들으면서 따라 했던 구조가 생각이 났고 우선 이 구조를 이용하자고 생각했습니다. controller, service, domain 패키지를 사용해서 배치했고 기능들은 domain->service->controller 순으로 사용되게 구현하였습니다. 이전보다는 객체화되었지만, 아직 부족함을 느꼈기에 2주 차 때 이 부분에 대해서 더 학습에서 적용해 보겠습니다.

[부족했던 부분]

  • Commit
    커뮤니티에서 함께-나누기 채널에 커밋에 관한 글이 올라왔었습니다. 커밋을 진행해야 하는 것은 알고 있었지만 커밋을 기능에 따라 작성해야 한다는 사실은 몰랐기에 많이 어색했습니다.
    중간중간 코딩을 진행하다가 빼먹거나 다시 돌아가서 커밋을 진행했던 부분이 많았고
    앞으로는 커밋하며 개발하는 습관을 들여야겠다고 생각했습니다.
  • Test
    테스트 코드를 어떤식으로 운영해야 하는지 어느 시기에 작성해야 하는지 몰라 많이 헤매었습니다. 왜 테스트 코드를 작성해야 하는지, 테스트 코드의 중요성에 대해서 다시 학습하고 작성 방법과 운영 방법에 대해 배워나가겠습니다.

패키지 구조 세분화


이렇게 클래스만 만들어 놓고 진행하던중 가독성과 프로그램의 구조를 더 확실하게 나타낼 수 있는 패키지 구조가 필요하다고 생각했고 이전에 Spring 공부할때 접했던 mvc 라는 패턴에 대해서 위에 첨부한 블로그를 중점으로 작성해보았습니다.

  • Controller
    서비스 객체를 멤버변수로 생성 후 서비스 객체의 메서드를 이용해 기능 수행
    사용자 입출력은 Controller에서 수행
    run메서드에 프로그램 전체 로직이 들어가도록 구현
  • Service
    도메인 / 비즈니스 로직은 이곳에서 구현한다.
    가장 작은 기능을 수행하는 메서드부터 구현하고 이들을 결합하며 점차 큰 기능을 수행하는 메서드를 만들어 나가기(private)
    가장 큰 단위의 기능을 수행하는 메서드가 Controller 단에서 호출되어 사용된다.(public)
  • Domain
    프로그램에서 객체라고 분리될 수 있는, 혹은 데이터를 저장해야 하는 기능이 필요한 경우 도메인 객체로 이를 유지하도록 한다.
    Domain 객체 내부에 값을 저장하기 전 대부분 validation 기능을 요구하므로 validation 기능 구현은 validation 클래스에, 사용은 domain 클래스에 하도록 한다.
  • Utils(final)
    유틸성 함수는 최대한 일반화 시켜서 기능을 구현할 것
    public static 메서드로 구현
  • Validation(final)
    입력값이 어떤 조건에 부합하는지 검증하는 기능 구현 (public static 메서드로 구현)
    특별한 경우가 아니라면 Validation 클래스에서 구현한 메서드들은 Domain 객체 혹은 Utils 메서드 내에서 사용
  • View(final)
    public static 메서드로 구현
    입력 메시지 (InputMessage)
    출력 메시지 (OutputMessage)
    출력 메시지의 경우 매개변수로 값을 넘겨 받아서 이를 출력하는 메시지도 구현한다. (단 출력 기능만 수행한다.)
    -제네릭
    -int와 long 대신 Long을 사용하는이유는 null을 나타낼 수 있기때문

코드 작성 및 리팩토링 기준

  • 요구사항이 변경되었을때에 유지보수성이 좋은 코드 인가
  • 책임과 역할을 잘 분리했는가
  • 코드가 대화하는 것처럼 자연스레 이어지는가
  • 객체 내부에서 상태와 행위를 관리하고 있는가
  • 객체의 분리가 제대로 이루어졌는가(해당 객체와 관련없는 로직은 없는지)

나만의 규칙

Controller

  • 서비스의 메서드를 사용해서 구현한다.
  • 입출력은 컨트롤러에 포함한다.

Service

  • 주 기능을 담당하는 부분
  • Domain클래스에 만들어진 메서드를 사용

Domain

  • 정보를 저장하는 클래스
  • 클래스의 속성변수를 사용하는 메서드라면 domain에 구현

Util

  • 기능을 쪼개다보면 공통적으로 사용될거같은 메서드들은 저장

Validator

  • 프로그램의 검증부분을 담당 주로 입력관련

view

  • 입력과 출력을 담당

진행하면서 배운점

  • 예외처리 하는방법
  • 커밋메시지 작성
  • 기능별 커밋 장점
  • 함수나 변수 네이밍(의미를 알 수 있게,축약하지말걸)
  • 함수당 하나의 기능에 집중
  • 들여쓰기 노력
  • 생활코딩
  • StringBuilder
  • 클린코드 규칙=객체지향 생활 체조
  • 접근제한자
  • 커밋을 위해서 다시 코드를 돌려 놓고 커밋하고 다시 원상태로 했는데
    커밋하고싶은 부분을 내가 설정할 수 있다는 것
  • 공통작업을 수행할 수 있는 기능을 갖는 것들을 Util이라는 클래스로 만들어서 재사용성 높이기
  • charSequence
  • 문자를 String으로 String.valueOf()
  • 테스트 코드 명
  • 기능 명세서를 더 세분화해서 기능을 구체적으로 -> 함수분리 , 테스트 만들기쉬움(TDD)
  • MVC 패턴
  • enum
  • 테스트 코드를 줄여주는 @ParameterizedTest
  • 정규식 : 특정 문자열의 규칙을 가지는 문자열의 집합을 표현하는 데 사용되는 언어
    참고
  • 테스트도 패키지화 해보자
  • 테스트 given/when/then
  • 테스트 하나를 만들때 마다 커밋
  • 기능 구현 하고 커밋 잊지 말기
  • 커밋은 코드를 보는 사람을 위하기때문에 확실하게 똑바르게

이 분의 회고록이 저한테 가장 큰 도움이 되었기에 저도 회고록을 작성해보았습니다!

profile
멋있는 사람 - 일단 하자

0개의 댓글