티스토리 뷰
우아한 테크코스 4기 프리코스 후기
부제 : 나는 모른다는 것을 알았다
우아한 테크코스 4기 프리코스에 합격해 지난 3주간 프리코스에 참가했다. 우테코 신청을 두고 고민할 때, 우테코에서 소개하는 코스의 지향점을 보고, 무엇보다 앞선 참여자들의 후기를 읽고 신청을 결심했기에, 나도 도움이 되고자 글을 남긴다. 주차 별 미션을 진행하며 배우고 생각했던 것들, 그리고 이 과정에서 내가 진정 얻은 것들에 대한 것들이 중심이다.
숫자 야구 게임
미션 저장소 https://github.com/woowacourse/java-baseball-precourse
내 저장소 https://github.com/hyewoncc/java-baseball-precourse/tree/hyewon
1주차에서 제시된 프로그래밍 요구사항
- indent depth를 2까지만 허용
- 3항 연산자를 쓰지 않는다
- 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만든다
그리고 프리코스 내내 지켜야 했던 사항이다
- Random값 추출과 사용자 입력은 지정 패키지의 지정 메소드를 활용한다
- 기능을 구현하기 전에 README.md 파일에 구현할 기능 목록을 정리한다
- 커밋은 위에서 작성한 목록 단위로 남기고, AngularJS Commit Message Conventions 를 참고해 커밋 로그를 남긴다
1주차 미션은 프로그래밍 난이도가 높지 않았다. 다만, 이제까지 신경쓰지 않았던 사항들을 지키는 것이 까다로웠다. 나만 그랬다면 참으로 민망한 일이겠으나, 평소에 커밋을 할 때 감으로 적당히 한 덩어리 나온 것 같으면
남겼다. 그래서 기능을 만들던 중간에 끊기기도 하고, 그렇게 다른 프로그램 코드를 보고, 며칠 뒤 돌아오면 도대체 뭘 하다 말았는지 알 수 없을 때가 종종 있었다.
프리코스의 지시를 따르면서, 미리 무엇을 구현할 지 기능 목록을 작성
하는 것, 그에 따라 단위 커밋과 의미있는 메시지
를 남기는 습관의 소중함을 알았다. 1주차 공통 피드백에서는 소통을 위한 의미있는 네이밍의 중요성
을 알려주신 게 인상 깊었다.
자동차 경주 게임
미션 저장소 https://github.com/woowacourse/java-racingcar-precourse
내 저장소 https://github.com/hyewoncc/java-racingcar-precourse/tree/hyewoncc
2주차에서 추가 제시된 프로그래밍 요구사항
- 함수의 길이가 15라인을 넘어가지 않게 한다
- else, switch/case를 쓰지 않는다
2주차 미션을 받고 기능 목록 작성을 끝냈을 때, 앞선 미션을 해서 그런가 체감 난이도는 더 쉬운데? 라는 착각을 했다. 프리코스를 시작하면서, 어느정도 코드를 짜기 전에는 앞선 기수의 사람들이나 다른 참여자의 PR 내 코드를 보지 않기로 정했었다. 그래서 충분히 짜 두고 열어봤다가 큰 충격을 받았다. 다른 사람들 보다 클래스를 훨씬 적게 나누었고, mvc 패턴 공부를 할 생각은 하지도 않았는데, 이걸 공부해 적용한 사람이 많았기 때문이다.
혼자 도출 해낸 이유는 객체 지향에 대한 이해가 부족하다
였다. 이해가 부족하니 자연스럽게 추상화된 역할과 개념을 프로그램 구조에 녹여낼 수 없었던 것이다. 자연스레 디자인 패턴 공부에 대한 필요도 느끼지 못했다. mvc 패턴은 웹에만 적용되는 것이라 생각했다.
그리고 2주차 피드백 메일을 보고 정말 찬물을 맞은 듯 정신이 확 들었는데, 그 중 정말 가슴에 깊이 새긴 사항들을 소개하겠다.
1. 비즈니스 로직과 UI 로직을 분리해라
비즈니스 로직과 UI 로직을 한 클래스가 담당하지 않도록 한다.
위 피드백에는 제대로 분리하지 못한 예시로 Car 객체에서 자신의 위치를 출력하는 코드가 있었고, 내가 정확히 그렇게 했었다. 심지어 StringBuilder를 사용한 부분까지 같아서 부끄럽지만 내 코드를 가져왔다.
public void printStatus() {
System.out.println(buildStatusString());
}
private String buildStatusString() {
stringBuilder.setLength(0);
stringBuilder.append(name).append('').append(':').append(' ');
for (int i = 0; i < position; i++) {
stringBuilder.append('-');
}
return stringBuilder.toString();
}
어쨌든 내 나름의 객체 지향적 사고
에서는, 역시나 부끄러워서 솔직하게 쓸까 말까 고민했지만, 음... 자동차가 자기 위치 소개를 하는 거지... 그러니까 당연히 Car가 말해야지..
대략 이런 사고로 한 일이었다.
그래서 단일 책임 원칙
을 포함해 객체 지향
의 개념에 대해 다시 곱씹는 시간을 가졌다.
2. 객체에 메세지를 보내라
상태 데이터를 가지는 객체에서 데이터를 꺼내려(get)하지 말고 객체에 메시지를 보내라
getter 메소드의 유혹은 강렬하다. 스프링을 인프런 김영한님의 강의를 들으며 공부했는데, getter와 setter사용 지양에 대해 자주 언급하셨다. 특히 불가피한 상황이면 getter는 몰라도 setter는 안된다고 하셨고, 이걸 불량학생인 나는 적당히 상황보고 써도 되는구나😏
쯤으로 곡해해 알아 들었다. get으로 꺼내지않으면 대체 어떻게 비교하는데? 에 대한 의문은 또다시 객체 지향
에서 답을 찾을 수 있었고 3주차 미션에 적용했다.
3. 필드(인스턴스 변수)의 수를 줄이기 위해 노력한다
필드의 수가 많은 것은 객체의 복잡도를 높이고, 버그 발생 가능성을 높인다.
인스턴스 변수로 꺼내 놔야 클래스 전체에서 쓰기 쉬우니까 하는 이유로 일단 뭐든 꺼냈던 지난날을 반성했다. 전체에서 쓰기 쉽다는 바로 그 이유 때문에 어디서 상태가 어떻게 변했는지 추적하기 어려운 상황에 종종 놓였음에도 별 수 없다며 관성적으로 코딩하던 습관을 고칠 수 있었다.
자판기
미션 저장소 https://github.com/woowacourse/java-vendingmachine-precourse
내 저장소 https://github.com/hyewoncc/java-vendingmachine-precourse/tree/hyewoncc
3주차에서 추가 제시된 프로그래밍 요구사항
- Coin 클래스를 활용해 구현한다
- Coin 클래스 필드 amount의 접근 제어자 private를 변경할 수 없다
3주차 미션은 글을 쓰는 지금 시점에서 아직 제출 마감이 되지 않았고, 당연히 피드백도 아직 없다. 그래서 미션을 진행하며 배운 것, 주의를 기울인 것을 쓰겠다.
1. Enum 클래스 활용
3주차 미션에서 Enum 클래스인 Coin을 활용하라는 요구사항이 추가되었다. Enum 클래스를 사용해본 적은 있었으나, 스프링을 RDBMS과 연동해 개발할 때 컬럼에 열거형 값을 저장하는 용도로만 사용했었다. 그래서 Enum의 정의와 사용 이유, 활용예를 찾아보았는데, 우형 기술 블로그와 테코블 포스팅에서 많은 도움을 받았다.
나는 Enum을 연관있는 상수값의 집합
으로 이해했다. Coin에는 각 동전 금액값이 있었는데, 주어진 pickNumberInList()
함수를 이용하기 위해 모든 금액값을 반환하는 것과, 금액값에 맞는 상수를 반환하는 코드를 짰다.
여기서 자판기가 소유하고 있는 동전 개수를 Coin 필드에 추가할까 고민했으나, 동전 개수는 랜덤으로 생성되니 상수값이 아니라고 판단해 별도의 CoinStock 클래스를 만들어 금액값과 개수를 저장했다.
2. Java 8 lambda
2주차 미션에서 우승자를 찾아내기 위해 이런 코드를 짰었다.
for (Car car : cars) {
if (car.getPosition() == winnerPosition) {
winnersName.add(car.getName());
}
}
구하는 로직이 객체 지향과 거리가 멀었던 것은 제쳐두고, 3주차에서는 람다를 적극 이용해 이런 식으로 바꾸었다.
List<String> winnersName = cars.stream()
.filter(car -> car.getPosition() == winnerPosition)
.map(car -> car.getName())
.collect(Collectors.toList());
indent가 줄었을 뿐 아니라, 훨씬 읽기 쉬워졌을 뿐 더러, 인스턴스 필드를 걷어낼 수도 있었다.
3. (또다시)객체에 메세지를 보내라
자판기에서 상품을 팔 때, 먼저 남은 투입 금액으로 이 상품을 살 수 있는지
확인하고, 그 다음 재고를 1 차감
해야 했다. 프리코스를 듣기 전의 나였다면 관성적으로 이렇게 짰을 것이다.
private int countOutProduct(Product product, int money) {
if (product.getPrice() > money) {
throw new IllegalArgumentException(NOT_ENOUGH_TO_BUY);
}
return product.setStock(product.getStock() - 1);
}
setter 메소드는 평소에도 사용을 지양 했기에 이렇게 하진 않겠지만, 메세지를 보낸다
를 부각하기 위해 이렇게 써보았다. 이번 과제에서는 메세지를 보낸다
를 적용해 이런 코드를 짰다.
private int countOutProduct(Product product, int money) {
if (!product.enoughMoneyToBuy(money)) {
throw new IllegalArgumentException(NOT_ENOUGH_TO_BUY);
}
return product.purchaseOne(money);
}
상품의 가격과 현재 돈의 비교를 상품 객체에 맡겨, 스스로 비교한 뒤 반환하게 하였다. 상품 구매 또한 현재 돈을 메세지로 주면, 상품 객체가 자신의 가격을 차감한 뒤 금액을 돌려준다.
4. 일급 컬렉션
일급 컬렉션은 개념 자체를 처음 접했다. 테코블에서 Enum 클래스 관련 글을 보고 다른 글도 읽고 있었는데, 해당 포스팅을 보고 당장 써먹어 봐야지! 하고 배우게 되었다.
테코블은 앞선 기수의 우테코 참여자들이 운영하는 블로그인데, 정말 많은 도움을 받았다. 상품 정보를 담고 있는 Product 객체를 모은 Products, 동전 금액과 개수 정보를 담고 있는 CoinStock 객체를 모은 CoinStocks 일급 컬랙션을 만들었다.
5. 그 외에도...
2주차 피드백에 있던 java api를 적극 이용
할 것, toString()을 오버라이딩
할 것 등을 적용했는데, 기초가 부족함을 통감했다.
3주차는 사실 문자열 입력에 대한 많은 예외 처리를 생각해내는 게 제일 고난이었는데, 다른 사람들도 그렇게 느끼는 듯 했다. 꼼꼼하게 한다고 했는데 자꾸 개발자라면 한 번은 봤을 법한 이 농담이 머리를 떠나지 않았다...
프리코스에서 얻은 것
사실 후기 포스팅에서 제일 하고 싶은 얘기는 이 항목이다. 프리코스를 참여하기 전에는 프리코스를 우테코 참여를 위한 2차 시험
으로 생각해서 완벽하게 코드를 짜겠다거니, 만점을 받겠다거니 했지만, 실제 참여해 본 프리코스는 3주라는 짧은 시간동안 크게 두 개의 깨달음을 얻은, 이 자체로 멋진 수업이었다. 앞서 3개의 미션 마다 적은 깨달음과 공부도 결국 이 두 개의 깨달음으로 수렴하는 내용이다.
1. '잘 읽히는 코드' 와 이를 추구하는 이유를 배웠다.
나는 미술을 전공했고, 본격적인 개발 공부를 졸업 후에 독학으로 시작했기에, 항상 팀 프로젝트
나 협업
에 대한 동경이 있었다. 실제로 외부 프로젝트에 참여도 해봤지만, 프리코스 참여 전에는 (클린 코드 책에 몇번이고 적혀 있음에도!🙃) 클린 코드
와 협업
을 긴밀한 관계로 생각하지 않았다.
하지만 프리코스 동안 클래스 이름
을 고민하고, 함수 이름
을 썼다 지웠다 모르는 사람이 읽으면 어떻게 들릴까 단어를 붙였다 뗐다가, 코드를 따라 읽으며 만약 내가 이 코드를 처음 읽는 사람이라면 읽는데 이해가 막히는 부분은 없는지
, 다른 사람이 이 함수를 이름만 보고 호출해 사용하는데 지장이 없을 정도로 잘 분리되고 이름 지어졌는지
정말 정신이 아득해 질 정도로 고민하면서 나는 협업
에 대한 준비가 안되어 있었구나 를 깨달았다.
2. 객체 지향을 모른다는 사실을 알았다.
글의 서두에 쓴 부제 이기도 한데, 객체 지향을 배웠다
도 아니고 굳이 모른다는 사실을 알았다
고 쓴 이유가 있다. 이제까지 웹개발에 포커스를 맞춘 독학을 했기에, 객체 지향 같은 개념에 대한 공부는 틈틈이 하는 부수적 공부로 생각했다. 그러다 보니 객체 지향에 대해 책도 읽고, 나름 공부했다고 생각했지만 실은 내가 제대로 이해하지 못한 부분에 대한 학습 메타 인지
가 전혀 없는 상황이었다. 프리코스를 하는 동안 내가 무엇을 모르고 있었는지
, 그래서 앞으로 무엇을 해야 할지
정말 크게 깨달을 수 있었다.
프리코스를 합격하고 안내 메일을 받았을 때, 미션과 간단한 가이드는 주겠지만, 공통 피드백을 참고해 주도적으로 학습하라
는 지침이 있었다. 당시에는 그렇군
하고 넘겼는데, 3주가 지난 지금 그렇게 말하신 의도를 알 것 같다. 프리코스는 나에게 앞으로 좋은 개발자가 되려면 스스로 어떻게 나아가야 하는가?
를 가르쳐 주었다. 오히려 간단한 가이드와 공통 피드백 정도의 힌트만 있었기에, 공부의 방향성을 스스로 깨닫고 충격을 받을 수 있었던 것 같다. (이런 의도가 아니었다면... 그래도 좋은 교훈을 얻었으니 만족합니다)
글 본문에 대한 관심도를 높이고 싶어서 처음에 일부러 밈을 활용한 다소 내용이 극단적인 짤을 첨부했는데, 사실 진심을 담은 버전은 이것이다.
피드백 메일에서 미션에 대해 고통스럽겠지만
하신 말에 3주차 미션을 마친 지금 과연 고통스러웠어!
하고 고개를 절로 끄덕이고 있다. 프로그래밍이 어려운 것도 있겠지만,
- 학습 상황에 대한 객관적 메타 인지
- 피드백을 인정하고 내가 학습해 코드에 담아내는 것
이것들이 실로 고통스러웠다! 하지만 이만큼 코드 한 줄에 신경을 쏟은 적이 오랜만이라 정말 정말 재밌는 시간이기도 했다. 3주차 피드백은 과연 또 나에게 어떤 충격을 줄지 두근거리면서 즐거움과 두려운 마음이 뒤죽박죽 함께 든다. 마치 롤러코스터의 낙하 지점을 앞둔 것 처럼... 이처럼 멋진 경험을 할 수 있도록 기회를 주고 프리코스를 준비하신 우아한테크코스 측에 감사드린다. 이 약속이 노고에 보답이 될 순 없겠지만, 프리코스의 경험으로 한 층 더 좋은 개발자로 성장하겠음을 확신하며 약속드린다.