우아한테크코스 프리코스 4기를 마치며 본문

그냥 이야기

우아한테크코스 프리코스 4기를 마치며

giron 2021. 12. 14. 22:02
728x90

매 주차 새로운 것을 배우고 적용해 보면서 제 자신을 바꾸기 위해 노력했다. 자신을 바꾼다는 게 생각보다 힘든 일이라는 것을 알았고 앞으로도 개발자의 길을 가는 데에 함께 해결해야 할 고민이라고 생각이 들었다.

프리코스를 진행하는 동안 코드에 대해서 많은 고민을 했습니다. 이전에는 클린한 코드보다는 코드의 내부 동작 방식 또는 새로운 기술에 관심을 주로 가졌다면 클린한 코드란 무엇인지가독성 좋은 코드는 어떻게 작성해야 하는지 등 새로운 관점을 배울 수 있었다.


3주간의 프리코스가 시험기간과 함께 하니 눈 깜빡한 사이에 지나갔다..😂😂😂

 

시험기간과 프리코스 기간이 정확히 겹치면서 오늘이 학교 종강일이자 프리코스 마지막 미션을 제출한 날이다..!🥳🥳

사실 마지막 주차는 아쉬움이 컸다. 변명 아닌 변명을 해보자면 시험이 4과목이나 겹치면서 진짜 공부하다가 리펙토링할거 생각나면 갑자기 노트북 켜고 리펙토링 하고 구현하고...ㅋㅋㅋㅋ 정신이 너무 없었다. 특히 마지막 주차에서 원래는 구현 기능 만들고 테스트 코드 작성 순서로 해야 했지만 시간 부족으로 구현도 못할까 봐 구현 먼저 끝내고 테스트 코드를 작성해서 아쉬움이 있었다..😅

하지만 재밌었다라고는 자신있게 말할 수 있었다. 프리코스가 아니었다면 어디서 150명 가까이의 사람들의 코드를 구경하고 후기를 나눠볼 수 있었을까. 

같은 프로그램을 구현하고 다양한 코드를 보면서 궁금증이 정말 많이 생겼다. 마음 같아선 한분 한분에게 왜 이렇게 작성했는지 물어보고 싶었다 ㅎㅎ🙄 서론으로도 할 말이 너무 많지만 일단 프리코스 3주 차까지의 후기를 남겨보겠다.

 

 

시작

처음 프리코스를 시작하면서 사실 조금은 자신이 있었다. 자바 스프링을 이용한 프로젝트도 여럿 진행해봤고 학교에서 객체지향 수업을 들어서 좋은 성적도 받아 봤고, 객체지향 실습 조교도 해본 적이 있어서 나름 자신 있다고 생각했다.

하지만 객체지향적인 '좋은 코드'를 작성하는데에는 많은 노력이 들어야 했다.

 

1주 차 - 숫자 야구 게임

내 PR주소: https://github.com/woowacourse/java-baseball-precourse/pull/388

2주 차 - 자동차 레이싱 게임

내 PR주소: https://github.com/woowacourse/java-racingcar-precourse/pull/370

3주 차 - 자판기

내 PR주소: https://github.com/woowacourse/java-vendingmachine-precourse/pull/124

3주 동안 배운 점

일급 컬렉션, MVC패턴, 테스트 코드, 자바 코드 컨벤션, code style, 자료구조, 살아있는 문서, 매직 넘버 등 너무 많다..

집중한 점

주어진 목적의식에만 집중

tdd, atdd 등 모두 적용하면 좋다고 생각한다. 하지만 프리코스 기간에는 자바, 클린 코드 본연에 집중하는 게 더욱 좋다고 생각했다. 객체지향적인 코드를 작성하는 데에만 집중해도 사실 부족한 부분을 많이 느꼈기 때문이다.☺️ getter 없이 구현하기부터 너무 어려웠다...(습관의 중요성)

 

Why?

개인적으로 다양한 참가자들의 코드들을 보면서 분석을 했다. 코드들을 보면서 하나하나 물어보고 싶었다. 예를 들면 회고록에도 적었지만 에러를 출력해주는 메시지 텍스트들을 public으로 한 클래스에 모은 이유? 또는 inner class를 작성한 이유? 등 궁금한 점이 많이 생겼다.

 

메시지 텍스트들을 public으로 한 클래스에 모은 이유?

개인적인 생각으론 메시지 텍스트를 public으로 모아두면 나중에 메시지를 호출할 때 IDE에서 자동완성으로 여러 개가 나와서 규모가 커지면 유지보수에 안 좋다고 알고 있었다. 하지만 많은 참가자들이 그러한 방식을 사용해서 내가 잘못된 지식을 가지고 있는가 싶어서 궁금하다. 혹시 이러한 이유를 알고 계시면 댓글로 남겨주셔도 좋을 것 같습니다!🤔🤔🤔

 

inner class를 작성한 이유?

inner class같은 경우는 프로젝트를 진행할 때, Dto를 사용할 때 주로 사용해봤다. 내부 클래스에서 외부 클래스에 쉽게 접근하고, 관련 있는 클래스를 묶어서 표현하므로 코드의 캡슐화가 좋기 때문이었다.

하지만 도메인 클래스에서 inner class를 사용한 분이 계셨는데 아직도 그렇게 구현한 이유를 못 찾고 있다. 혹시 아신다면 댓글...부탁드립니다.

 

디자인 패턴

이번 미션까지 하면서 많은 참가자들의 코드들을 보면 대부분 MVC패턴을 사용했다. 사실 가장 흔하고 구현하기 쉽다고 알려져 있기도 해서 3주 차 요구사항인 UI와 비즈니스 로직을 분리하는데 많이 사용하신 것 같다. 나는 기존에 MVC패턴을 알고는 있었지만 적용해 본 적이 없었다. 이번 미션에서 적용해보면서 단일 책임 원칙에 대해서도 공부할 수 있었는데 읽을 때는 당연하다는 듯이 이해했지만 막상 구현하면 역시 쉽지 않았다. 디자인 패턴을 알면 단일 책임 원칙을 지키는데 더 편하다고 생각이 들어 다음 최종 코테때까지 디자인 패턴을 통한 클래스 분리를 계속 연습해보려고 한다. 

 

배운 점

1. 정규식

 우선 입력값 처리부터 [콜라,1000,3];[사이다,1500,5] 처럼 입력값에 형식이 정해져 있어서 입력값 처리부터 예외처리를 잘해야 했었다. 처음 입력의 틀이 주어진 것을 보고 정규식을 이용해야겠다고 생각했다. 학교에서 파이썬을 이용한 정규식 수업을 해서 금방 해결할 수 있겠다고 생각은 했는데 자바에서는 파이썬의 문자열의 시작과 끝이 정확히 일치될 때 반환하는 fullmatch함수가 없어서 생각보다 오래 걸렸다. [ ] 는 구별할 수 있었지만 [ ];[ ] 모두 검증하기에는 어려웠다. 따라서 세미콜론을 검증하는 로직과 [ ]를 검증하는 로직을 분리해서 두 번에 걸쳐서 검증하도록 구현을 했다.

정규식을 이용하니 확실히 예외처리 로직이 간결해졌다.

 

2. 검증과 예외 분리

2주 차 미션까지는 예외 처리가 많이 없어서 예외를 발생하는 곳에서 검증을 함께 진행했다. 하지만 이번 주차 미션부터는 예외 처리할 사항이 많아서 검증하는 로직과 예외를 발생시키는 로직을 분리했다. 이렇게 하니 에러를 출력하는 메시지와 검증을 위한 변수들이 구분이 되어서 코드가 길어지더라도 가독성이 좋고 유지보수도 좋을 것 같았다.

또한 Input 받을 때 발생하는 예외처리 의외에 자료구조에 저장해 둔 자료를 찾을 때 없는 자료가 있다면 NotFound~의 이름의 예외를 따로 만들어서 구분했다.

 

3. 함수가 한 가지 일만 하도록 작게 만들기

이번 미션의 커밋 기록을 보면 함수를 분리하는 리펙토링을 종종 볼 수 있을 것이다. 구현할 때는 이 정도까지 한 가지 일을 처리한다고 생각했지만 막상 구현을 하고 코드가 길어져서 내용을 읽어보면 더 분리가 가능한 것을 알 수가 있었다. 

 

4. 자료구조

코드를 작성하면서 자료구조의 중요성을 많이 알게 되었다. 3주 차 미션을 진행하면서 기능 구현 사항과 예외 처리사항이 많아지면서 함수의 크기가 커져갔다. 예를 들어 private static Map<Coin, Integer> changes = new TreeMap<>(); 와 같이 사용했다. 기존에는 map 자료구조 중 HashMap을  사용했다. 해시로 구현되어 있기 때문에 속도가 빨라서 자주 사용했었는데 이번 미션을 하면서 잔돈을 출력할 때 순서대로 출력해야 하는 것을 보고 정렬이 되어있는 Map 자료구조를 찾다가 TreeMap을 알게 되었다. TreeMap을 사용하니 기존의 map에서 List에 옮기고 정렬을 하고 출력하는 방식에서 바로 출력할 수 있어서 불필요한 코드를 많이 줄였다.

 

5. 람다식

기존의 for문
람다식을 이용한 중복 검증 로직

람다를 사용하면 IDE에서 지원해주는 기능과 더불어 가독성도 좋다. 또한 set을 따로 구현하고 for문을 돌면서 set함수에 넣는 로직을 .collect(Collectors.toSet())으로 만들어주면서 더욱 간결하게 구현할 수 있었다.

 

6. 객체에 메시지를 보내라

즉, getter사용을 지양하고 도메인 클래스 로직에서 처리하라는 뜻이다. 기존에 항상 getter를 이용한 습관이 있어서 이 습관을 고치는 게 힘들었다. 하지만 객체에 메시지 보내기를 적용해보니 코드의 가독성이 많이 좋아졌다. 또한 객체지향 원칙 중 하나인 디미터(Demeter)의 법칙(“친구하고만 대화하라”) 을 지키는 데에도 도움이 되었다.

 

7. 일급 컬렉션

public class Cars {
	List<Car> cars;

	public Cars() {
		this.cars = new ArrayList<>();
	}

	public void join(String carNames) {
		try {
			String[] validCarNames = InputException.isValidCarNames(carNames);
			for (String carName : validCarNames) {
				cars.add(new Car(carName));
			}
		} catch (IllegalArgumentException e) {
			System.out.println(e.getMessage());
			join(InputView.getInputRaceCar());
		}
	}

	public List<Car> getCars() {
		return cars;
	}

	public Car getCarWithMaxPosition() {
		return cars.stream().max(Comparator.comparingInt(Car::getPosition)).get();
	}

일급 컬렉션은  ‘비즈니스에 종속적인 자료구조'를 구현한다는 게 큰 장점이다. car를 관리하여 비즈니스 로직을 점검할 때 Cars 클래스만 확인하면 유지보수가 가능하기 때문에 상당히 효율적이었다. 

공부 출처: https://jojoldu.tistory.com/412

 

8. SOLID원칙

이번 프리코스에서 가장 강조된 부분이지 않았나 싶기도 한다. 1주차는 함수의 분리 2주차는 클래스와 함수 분리 3주차 또한 클래스를 분리하여 유기적으로 연결시키도록 하는 훈련이었다. 사실 스프링부트로 프로젝트를 할 때는 domain, service, controller가 명확히 구별되어서 쉽게 구별할 수 있었다. 그래서 이번 미션 동안 직접 MVC패턴에 맞춰 나눠보면서 헷갈리거나 어려운 부분들이 있었다. 

아래 블로그에 설명이 잘 되어있다. 계속해서 반복 학습으로 자동으로 SOLID원칙을 적용할 수 있도록 의식하고 연습해야 할 것 같다.

공부 출처: https://limkydev.tistory.com/77

 

9. Enum

마지막 미션에서는 Enum타입이 주어지고 시작을 했다. enum타입을 프로젝트에서 사용할 땐, 소셜 로그인을 어디로부터 로그인했는지 또는 성별과 같은 단순한 형태로 사용했었다. 하지만 이번 미션에서는 동전을 카운팅 해서 거스름돈을 반환도 해야 했어서 enum타입에 대해 추가적으로 학습을 해봤다. 생각했던 것보다 enum을 이용해서 다양한 구현이 가능했고 그러한 구현으로 책임을 분리하는 방식이 가능했다. 객체 간 책임 분리.. 앞으로 더 공부해야 하겠다.

이번 공부 출처 또한 우아한 형제들의 이동욱 님의 블로그를 참고했다.

공부 출처: https://jojoldu.tistory.com/137?category=635881

 

최종 코테를 준비하면서 적용할 것들.

지금까지 미션들을 회고해보니 아쉬운 점이 한 두가지씩 계속 보여서 최종 코테때는 꼭 전부 적용해서 더욱 보기 좋은 코드를 만드려고 한다. 대표적으로 정적 팩토리 메서드toString이다.

 

정적 팩토리 메서드는 프로젝트를 진행할때는 잘 적용했는데 이번 미션들을 돌이켜 보니 그냥 생성자로 구현을 했던 것들이 보였다....(왜그랬지...) 그래서 마지막 최종 코테때는 꼭 기회를 살려서 정적 팩토리 매서드를 사용해서 코드의 가독성을 높일 계획이다.

 

그리고 toString Effective Java에서는 toString을 항상 재정의하라고 되어있다. 사실 Effective Java를 보기 전에는(아직 전부 보지 못했지만..) 스프링 부트를 이용한 프로젝트를 진행해서 그때는 엔티티에 toString을 재정의하면 연관관계에서 순환 참조가 발생할 수도 있었다. 또한 도메인 클래스에 뷰와 관련된 코드라고 생각되어서 부정적이었다.

하지만 자바코드를 작성하는데 toString을 재정의해서 사용하면 가독성에 좋다고 생각이 되어서 한번 적용해보려고 한다. 또한 2주차 피드백에도 객체의 상태를 보기 위한 로그의 성격이 강하면 사용해도 좋다라고 되어있었다! 만약 최종 미션에 toString이 필요하다고 판단되면 적극 사용할 계획이다!!👀


이 외에 피드백 등을 포함하면 정말 많은 객체지향에 대한 지식을 얻었다. 배운 내용들을 정리해보니 양이 너무 많아서 1주 차, 2주 차, 3주 차로 분리를 해야 할 것 같다..😄😄

 

마지막 코딩 테스트가 남았다. 걱정 반 기대 반이다. 내가 지금까지 성장해 온만큼만 잘 보여준다면 후회 없이 프리코스를 마무리할 수 있을 것 같다. 처음 프리코스를 시작하고 메일을 받았을 때 글귀가 있다.

우아한테크코스가 목표가 아니라 좋은 개발자로 성장하는 것을 목표로 하면서 성장해 나가는 과정으로 생각하고 도전했으면 합니다.

프리코스를 진행하다 보니 우테코를 목표로 코드를 작성하고 있다는 느낌이 들었다. 눈에 들고 뽑히기 위해서 남들이 작성한 예쁜 코드를 복사하고 붙여 넣었다. 잠깐 동안 IDE에 코드를 붙이고 멈췄다. 이렇게 해서 예쁜 코드를 작성해서 나에게 남는 게 뭘까? 우 테코에 뽑히고 그때 공부하면 되지 않을까? 이런 생각은 잠깐이었지만 나를 부끄럽게 했다. 

위 글귀를 보면서 다시 한번 나를 되짚어보고 어떤 코드를 작성하든, 남의 코드를 인용하든 그 코드를 사용하는 이유를 알고 사용 하자고 다시금 되새김할 수 있었다. 

 

마지막으로는 제가 매주 미션을 진행하면서 의식하고자 가져온 리스트가 있습니다. 이 리스트를 보면서 이 글을 읽으시는 독자분들도 클린 코드를 작성하는 데에 기준을 잡으시면 좋을 것 같습니다.

<나의 관습을 버리자>
자바 코드 컨벤션을 지키면서 프로그래밍했는가?
한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가?
else 예약어를 쓰지 않았는가?
모든 원시값과 문자열을 포장했는가?
콜렉션에 대해 일급 콜렉션을 적용했는가?
3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가?
getter/setter 없이 구현했는가?
핵심 로직을 구현하는 도메인 객체에 getter/setter를 쓰지 않고 구현했는가?단, DTO는 허용한다.
코드 한 줄에 점(.)을 하나만 허용했는가?
메소드의 인자 수를 제한했는가?4개 이상의 인자는 허용하지 않는다.3개도 가능하면 줄이기 위해 노력해 본다.
디미터(Demeter)의 법칙(“친구하고만 대화하라”)을 지켰는가?예를 들어 location.current.representation.substring(0, 1)와 같이 여러 개의 점(.)이 등장하면 리팩토링할 부분을 찾아본다.
메소드가 한가지 일만 담당하도록 구현했는가?
클래스를 작게 유지하기 위해 노력했는가?

 

 

 

 

 

728x90
Comments