JAVA/우아한테크코스 4기

[211206] 우아한테크코스 프리코스 2주차 후기

kth990303 2021. 12. 6. 20:27
반응형

1주차 후기: https://kth990303.tistory.com/220

 

이번 2주차 미션은 3기 기수분들도 작년에 진행했던 '자동차 경주 게임'이었다. 

자세한 요구사항은 https://github.com/woowacourse/java-racingcar-precourse 로 들어가면 확인할 수 있다.

 

여러 대의 Car 객체를 이용하여 클래스 분리 능력을 키워주려는 목적이 보이는 듯했고, 에러 처리 또한 try-catch를 이용하면서 indent를 보다 빡세게 제한하겠다는 의도가 보이는 미션이었다.

1주차 때와 다른 점이라면, 게임이 끝난 후 재시작을 할 필요가 없다는 점.

 

내 코드는 https://github.com/woowacourse/java-racingcar-precourse/pull/356 여기서 볼 수 있다.

이번 2주차 또한 1주차와 마찬가지로 하나의 메소드는 하나의 기능을 하는 것의 중요성을 느끼며 진행했다. 이는 1주차 프리코스 후기글에도 썼기 때문에 여기에 추가적으로 쓰진 않겠다.

이번에는 1주차 때보다 더 나아진 나의 모습들, 그리고 고민했던 내용들을 기록하려 한다.


static 메소드, Validator에선 써도 되지 않을까?

1주차 때에도 상당히 고민했던 내용이었지만, 이번 주차때에 정말 많은 고민을 했다.

1주차 때는 숫자야구 정답을 맞춰도 다시 재시작하는 과정에서 검증 클래스들을 생성하고 삭제하는 과정에서 new를 이용한 불필요한 생성자 호출이 많아질 것이라 판단하여 static 메소드를 이용했었다.

 

그러나 이번 주차에는 게임을 재시작하지 않기 때문에 검증 클래스들을 처음 시작할 때 한번 생성해주면 됐기 때문에 굉장히 많은 고민을 했다.

 

결론을 먼저 말하자면 이번 시간에도 static 메소드를 사용했다!

static 메소드는 사실 권장되는 방법은 아니다.

단점들이 꽤 있는 편인데, 이 블로그에서 상세히 볼 수 있었다. https://kellis.tistory.com/127 

 

Static 사용을 피해야 하는 이유

Java에는 static이라는 키워드가 존재하며, 이는 static으로 지시된 특정한 멤버가 해당 클래스의 인스턴스가 아니라 클래스 자체에 속해 있음을 나타냅니다. 즉, 클래스의 모든 인스턴스에서 공유

kellis.tistory.com

그럼에도 불구하고 내가 이번에 검증 메소드를 static 메소드를 사용한 이유를 아래에 써보겠다.

 

 1. 일단 검증 작업은 사용자 입력을 필수적으로 받아야 하는 게임 특성 상, 전체 프로그램 라이프사이클 내에 필수적으로 존재해야 한다. 따라서 오히려 생성과 소멸을 하는 작업에서 Garbage Collector의 인식 대상이 돼 불필요한 성능 저하를 야기할 수 있겠다는 생각을 했다.

자바의 Math.max, Math.min 함수를 생각해보자. 우리가 따로 Math m = new Math(); 와 같이 인스턴스를 생성하면서 위 함수를 쓰진 않는다. 이렇게 명확한 이름을 가진다는 점은 생각보다 큰 장점이다. 사용자들에게 편리하고 명확하게 다가올 수 있기 때문이다. 이는 이펙티브자바 2장에 적힌 내용이기도 하다.

검증 작업도 마찬가지로 클래스명이 명확하다면 오히려 static 메소드로 하는 게 괜찮지 않을까 생각하여 static 메소드를 이용하였다.

 

 2. 멀티쓰레드 때 safe하지 않다는 단점, test하기 어렵다는 단점도 있다.

그렇지만 검증 메소드는 어떤 변수값을 바꾸거나 도메인에 영향을 미치는 메소드가 아니다. 말 그대로 검증한 후, 문제가 있을 경우 IllegalArgumentException을 throw 해주며, 문제가 없을 경우 그대로 return하는 메소드이다. 즉, 변화를 일으키는 로직이 아닌 말 그대로 검증만 해주는 메소드란 것이다. 따라서 잘못된 변화 및 그에 따른 유지보수 저하를 걱정하지 않아도 된다고 판단하였다.

 

 3. 오버라이딩이 불가하다는 단점이 있다. 이 부분은 사실 고민을 좀 했다. 아예 Validator 인터페이스를 만들고, 그에 따른 구현체를 여러 개 만드는 것이 가독성에 좋지 않을까 생각하긴 했으나, 문제는 Exception을 판단하는 객체의 타입이 하나가 아니라는 점이다.

Cars 객체들을 validate하는 클래스는 이름이 중복인지, Car 객체는 총 몇 대 들어있는지 판단해야 했기 때문에 List<Car> 타입을 검증하는 클래스이고, 시도횟수를 validate하는 클래스는 String 타입을 검증하는 클래스이다.

따라서 인터페이스에서 여러 오버로딩된 메소드들을 선언하고, 구현체에 사용해야 되는데, 인터페이스에 선언된 메소드들은 반드시 구현체에 사용돼야 하므로 불필요한 오버로딩 메소드들을 구현체에서 사용하게 돼버리는 단점이 생겨난다.

따라서 인터페이스를 사용하지 않기로 결정. 

 

(+21.12.08. 추가: 4번 내용은 2주차 미션 제출 후 주어진 공통피드백을 본 후, 생각이 바뀌었다. 그 이유는 최하단에 추가하겠다.)

 4. 결정적으로, 현재 프로젝트 규모 자체가 크지 않다. 물론 앞으로의 유지보수를 생각해서 어느 정도 중대형 프로젝트 유지보수용으로 적절한 구조로도 만들 수 있겠지만, 개인적으론 너무 지나치게 먼 미래에 맞는 프로젝트 구조로 만들기보단, 상황에 따른 유동적인 프로젝트 구조 설계를 중요시 여기는 타입이기 때문에 불필요한 mvc 구조라든지, 불필요한 생성자 호출을 사용하지 않기로 결정하였다.

(스프링을 찍먹하면서 사용했던 mvc 구조를 이번 프로젝트에 적용하지 않은 이유이기도 하다.)

 

static 메소드를 사용할지에 대해선 굉장히 많은 고민을 했기 때문에 괜찮은 참고 링크들을 남겨두겠다.

인터페이스와 static메소드: https://dahyeee.tistory.com/entry/JAVA-interface-default-static%EB%A9%94%EC%86%8C%EB%93%9C

static 메소드 성질: https://ibks-platform.tistory.com/288

static 메소드 단점: https://kellis.tistory.com/127

이펙티브자바 2장 요약: https://junshock5.tistory.com/125

이펙티브자바 2장 요약(2): https://sjh836.tistory.com/168


Github Merge, Rebase, 그리고 깃허브 컨벤션과 리드미

1주차 때에도 github 컨벤션에 따른 커밋메시지 키워드와 리드미를 신경쓰긴 했지만, 

2주차 때엔 더 깊게 고민해보는 시간을 가졌다.

 

우선 1주차 때에는 키워드를 처음 접했어서 그런지 chore을 써야 할 키워드에 fix를 쓰고 있었고,

refactor과 style을 혼동하기도 했다. (사실 refactor과 style은 아직도 조금 헷갈린다.)

 

이번 2주차 때엔 그런 일이 없도록 키워드를 보다 꼼꼼히 체크하였다.... 고 생각했으나!

커밋 메시지 키워드를 또 내 착각으로 잘못 적는 불상사가 일어나고 말았다.

테스트 코드 수정인데 키워드로 test가 아닌 fix를 적어놓은 나란 놈은...

그렇기 때문에 인텔리제이의 Edit Commit Message 기능 (우측클릭 - 단축키 F2)를 이용하여 test로 고쳐주었더니, 위 그림과 같이 또다른 branch가 생겨났다. (브랜치명은 기존 브랜치명과 같으나, 갈래가 나뉘어져 버렸다.)

따라서 이를 merge해주려고 했는데, rebase와 merge 이렇게 둘 중 선택하라는 인텔리제이의 명령이 내려왔다.

 

나는 보라색 브랜치를 노란색 브랜치 위에 덮어쓰기를 원했고, 그렇게 하려면 rebase를 택했어야 했는데,

rebase를 이 때 처음 접한 나는, 어차피 코드를 합쳐야되니까 merge 해야지~ 하면서 merge를 클릭해버렸다...

그랬더니 노란색 Commit 이력과 보라색 Commit 이력이 둘 다 남아버려서 커밋메시지 이력이 뻥튀기가 돼버려 깔끔하지 못한 이력을 남겨버렸다.. 다행인 점은 실무에서 이러지 않았다는 점. 아직 나는 깃허브 기능의 반도 모르고 있다는 사실을 절실하게 깨닫게 된 사건이었다.

 

별개로 깃허브에는 별별 기능이 다 있었다. rebase 뿐만 아니라 cherry-pick 등 다양한 기능들이 있었는데, 이후에 기회가 되면 cherry-pick에 대해서도 공부해봐야겠다. 

 

 

추가로, 1주차 때보다 리드미를 이쁘게 작성하도록 노력하였다.

2주차 리드미

`` 를 이용해서 주요 키워드에 박스를 쳐주었고,

** ** 를 이용해서 특정 부분을 볼드체 해주었다.

그리고 리드미 만으로 클래스 특징과 구조 설계를 파악할 수 있도록 1주차 때에 비해 간략하면서도 핵심을 적을 수 있도록 노력하였다. 


(+ 21.12.08. 추가)

2주차 공통 피드백 내용

3주차 미션 안내와 함께 2주차 공통 피드백이 메일로 전달됐다.

 

제일 눈에 띄었던 것은, 객체 내 정보를 view쪽에서 print하는 로직과 service 처리로직을 하나의 클래스에 공통으로 삽입하지 말라는 것이었다. 즉, 비즈니스 로직과 UI 로직을 분리하라는 것이었다.

이는 단일 책임의 원칙을 지키는 방법이기도 하다.

 

나는 Car 객체의 전진 기능 메소드와 몇 칸 전진했는지 출력해주는 메소드를 Car 클래스에 포함시켰었는데,

이렇게 할 경우 단일 책임의 원칙에 어긋나게 되는 것이다.

이러한 이유 때문에 domain과 service, controller 분리를 철저하게 하는 mvc(model, view, controller) 패턴을 사용하는 것이었다.

 

또한, 필드(인스턴스 변수) 수가 너무 많으면 객체의 복잡도, 버그 발생 가능성이 높아지므로 이 점에도 주의해야 할 듯하다.

클래스를 세세하게 분리하는 것이 유지보수에 좋은 이유기도 하다.

그동안 함수 분리는 (개인적으론) 잘해왔다고 생각했는데, 클래스 분리는 좀 더 수련이 필요한 것 같다.

 

또한, 객체에서 데이터를 꺼낸다기보단, 객체에 메시지를 전달하라는 내용도 인상깊었다.

그렇다.

객체지향 책들을 읽으면서 공부하려고 해도, 실전에서 여러 번 부딪히면서 얻는것보다 못하다는 말이 맞는 듯하다.

또, 실전에서 부딪히면서 겪은 후에 객체지향 이론서들을 읽으면 '아는 만큼 보인다' 라는 말이 있듯이 또 다르게 느껴지는 듯하다. 

이번 프리코스가 끝나고 최종 코테 결과가 어떻든 간에 다시 한 번 객체지향 이론서를 읽어봐야겠다.


이번 2주차는 이렇게 마무리하였다.

1주차와 유사한 난이도였기 때문에 부담없이 재밌게 작성했으며,

스프링 웹개발을 찍먹했던 시절이 떠올라 mvc패턴을 이번에도 적용해볼지 고민해보며 구조와 설계에 고찰하면서 성장할 수 있었던 좋은 기회였다.

 

3주차 미션이 상당히 어렵다던데 걱정되면서도, 한편으로는 기대가 된다.

3주차 미션, 그리고 최종 코테를 무사히 마칠 수 있도록 열심히 공부해야겠다.

 

여담으로 최종 코테를 잠실에서 오프라인으로 치룰 수도 있다고 한다.

제발 그랬으면 좋겠다... 하지만, 코로나 상황이 너무 심해졌기 때문에 불가능에 가깝지 않을까 예상해본다 ㅠㅠ

(+ 21.12.08. 추가: 최종 코테는 온라인으로 진행된다.)

반응형