많이 늦은 회고
프리코스 4주차 제출이 끝나고, 그 동안 미뤄둔 일들을 차례대로 쳐내느라 4주차 회고를 잠시 미루고 있었다
이제라도 쓰는 4주차 회고
글 시작부터 말하자면... 테스트만 통과하는 쓰레기를 만들어버렸다!
정말 힘든 미션이었는데 그마저도 모든 요구사항을 만족하지 못해서 정말 속상하다...
슬프지만 이럴 때 회고를 작성해야 더욱 도움이 되기에 작성해봐야겠다..
4주차 시작에 앞서 3주차 로또 미션에 대한 리뷰를 진행했다
앞선 회고에서 말했듯이 3주차 미션에서 가장 고민을 많이 했던 부분이 재입력 로직에 관한 부분이었다
많은 고민을 해봤지만 입력 부분을 메서드별로 따로 빼서 try-catch
를 통해서 예외 처리를 해주는 방법밖에 생각나지 않았다
private Money inputMoneyWithRetry() {
while (true) {
try {
return new Money(InputView.inputMoney());
} catch (IllegalArgumentException e) {
OutputView.printErrorMessage(e.getMessage());
}
}
}
private WinningNumber inputWinningNumberWithRetry() {
while (true) {
try {
return new WinningNumber(InputView.inputWinningNumber(), InputView.inputBonusNumber());
} catch (IllegalArgumentException e) {
OutputView.printErrorMessage(e.getMessage());
}
}
}
중복되는 로직을 별개의 메서드로 빼는게 좋지 않다는 생각이 들었는데 다른 방법을 찾지 못해서 어쩔 수 없이 구현하고, 리뷰를 통해 방법을 알고자 했다.
그렇게 리뷰를 하면서 발견한 방법이 함수형 인터페이스
를 활용한 방법이다!
송선권
님의 코드리뷰를 하면서 배울 수 있었고, 다음 미션에서 적용해보기로 했다!
그렇게 가장 고민했던 부분을 해결할 수 있었고, 마지막 미션인 편의점 미션
을 시작하게 되었다
처음 요구사항을 보고 깜짝 놀랐던 기억이 있다!
끝을 모르는 스크롤과 수많은 글자들을 보고 놀라서 잠시 창을 닫았다
분명 저번주차 회고에서 TDD
와 5시간 내에 개발을 목표로 했지만 이 양은 그 시간으로 커버가 안될것 같았다!
실제로도 그랬다.. 5시간은 커녕 50시간을 해도 해결하지 못했다! 테스트는 통과하지만....
이럴수가!!!!
결과만 보면 그렇다
이제 한번 회고를 해봐야겠다
너무 스파게티 코드라서 코드적인 설명은... 안해도 될것 같다
다만 이번 4주차 과제를 진행하면서 느꼈던 점이 있다
그것은 바로 그 동안 진행해온 1,2,3주차의 미션에서 강조하고자 했던 내용들이 4주차 미션에 모여있다는 것이었다
3주간의 빌드업을 4주차 편의점 미션에서 펑 터뜨린것 같다!
잠깐 내가 생각한 각 주차 미션에서의 강조하고자 했던 점을 얘기해보자
1주차 미션은 계산기 미션
이었는데, 이 미션에서 가장 중요하다고 생각했던 점은 바로 정규식
을 이용한 파싱이다!
split
을 이용한 파싱은 그래도 좀 해봤는데 정규식을 이용한 파싱은 처음이었다.
실제로 1주차뿐만 아니라 2주차의 이름 파싱
, 3주차의 로또 번호 파싱
, 4주차의 상품명 - 수량
파싱 등 모든 주차의 미션에서 사용했다.
그리고 1주차 미션에서 가장 많이 고민했던 검증의 책임
에 대해서도 이후 주차에서 적용했던 것 같다!
1주차에서는 검증의 책임
을 분리해서 외부 클래스에서 검증을 했는데 프리코스를 진행할수록 어떤 값에 대한 검증은 해당 클래스 내에서 하는게 맞다는 생각이 확실하게 들었다
사용하는 클래스에서 검증을 해줘야 클래스가 늘어나지 않고 검증하는 로직같은 것들을 숨겨서 사용할 수 있지 않을까? 라는 생각이 들었고 다른 분들과 코드리뷰를 할 때 . 이부분들에 대해서 더욱 많이 질문했던 것 같다!
검증의 책임에 꽂혀서 이것만 질문하고 다녔던것 같은데 찾지는 못하겠다..
2주차 미션은 자동차 경주
미션이었다.
이 미션은 이전에 해본 적이 있는 미션이었다.
작년 6기 프리코스도 그렇고 개인적으로도 많이 해 본 미션이었다.
그래도 아예 모른다는 마음을 가지고 요구사항을 처음부터 읽어보며 진행했다.
이전에 미션을 진행할 때는 보이지 않았던 점들이 보이기 시작했다.
그만큼 성장했다는 뜻이겠지
2주차 미션에서 가장 중요하다고 생각했던 점은 전략 패턴
과 클래스 분리
이다.
전략패턴
부터 말해보자
학교 객체지향개발론 및 실습
수업에서 배운 디자인패턴이었다.
실행시간에 로직이 변경될 수 있으니 자동차의 움직임을 변경하는 로직을 따로 분리해서 넣어주는 방식으로 진행했다.
이렇게 하니 로직의 변경에 대응할 수 있을뿐만 아니라, 단위 테스트에서도 용이하게 사용할 수 있었다.
해당 미션에서는 랜덤 값을 통해서 자동차의 움직임을 제어해줬는데 그렇게 되면 테스트 환경에서 내가 원하는대로 자동차의 움직임을 제어할 수가 없었다.
그래서 해당 로직을 따로 빼서 테스트용 숫자 생성기를 따로 만들어서 진행했었다.
그렇게 하니 원하는 방향으로 테스트할 수 있었다
@Test
void 전진_테스트() {
assertSimpleTest(() -> {
Car car = new Car("junki", new Num4Generator());
car.move();
assertThat(car.getPosition()).isEqualTo(1);
});
}
@Test
void 정지_테스트() {
assertSimpleTest(() -> {
Car car = new Car("junki", new Num0Generator());
car.move();
assertThat(car.getPosition()).isZero();
});
}
public class Num0Generator implements NumberGenerator {
@Override
public int generateNum() {
return 0;
}
}
public class Num4Generator implements NumberGenerator {
@Override
public int generateNum() {
return 4;
}
}
이런 방식으로 진행했다.
그리고 하나 더 의도한 부분은 클래스 분리
라고 생각한다.
프리코스를 진행하면서 배운 것들 중에서 가장 좋은 부분같다
기존에는 Car
클래스가 있으면 name
이나 position
같은 값들을 그냥 단순 멤버변수로 가지고 있어 Car
클래스 내에서 모든 멤버변수들에 대한 검증을 진행했다.
하지만 2주차 미션을 진행하면서 일급 객체
라는 개념을 알게 되어 적용해봤다.
name
이나 position
같은 값들을 별도의 클래스로 분리를 하고 해당 값들에 대한 검증을 클래스 내에서 진행을 하니 Car
클래스에 있던 수많은 검증 로직들을 분리할 수 있었고, 클래스의 역할만 나타낼 수 있었다.
이후에 진행된 모든 미션들에 대해서도 일급 객체
와 일급 컬렉션
의 개념을 이용하여 많은 값들을 따로 클래스로 빼내어 적용해볼 수 있었다.
public class CarName {
private final String name;
private static final int NAME_LIMIT_LENGTH = 5;
private static final String engilshRegex = "[^a-zA-Z]";
public CarName(String name) {
this.name = name;
validateName();
}
private void validateName() {
validateNameSize();
validateNameNonEnglish();
}
private void validateNameSize() {
if (name.length() > NAME_LIMIT_LENGTH) {
throw new IllegalArgumentException("자동차의 이름은 5글자를 초과할 수 없습니다!");
}
}
private void validateNameNonEnglish() {
Pattern pattern = Pattern.compile(engilshRegex);
Matcher matcher = pattern.matcher(name);
if (matcher.find()) {
throw new IllegalArgumentException("이름에는 영어를 제외한 다른 문자가 들어갈 수 없습니다!");
}
}
public String getName() {
return name;
}
}
처음으로 적용해본 CarName
이라는 일급 객체
public class Buy {
private final int buy;
public Buy(String buy) {
this.buy = Validator.validateNum(buy);
}
public int getBuy() {
return buy;
}
}
public class Get {
private final int get;
public Get(String get) {
this.get = Validator.validateNum(get);
}
public int getCount() {
return get;
}
}
public class Price {
private final int price;
public Price(String price) {
this.price = Validator.validateNum(price);
}
public int getPrice() {
return price;
}
}
편의점 미션에서 적용해 본 일급 객체들
3주차 미션인 로또 미션
에서 가장 중요하다고 생각했던 점은 재입력에 관한 로직이었다.
위에서 말한 것처럼 미션을 진행하면서 구현을 할 때는 모든 입력 로직에 대해서 try-catch
문을 감싸주고 controller
내에 분리된 메서드들을 뒀었다.
그렇게 되니 중복되는 코드가 많고 지저분하다는 느낌을 너무 받았다.
그렇게 제출을 하고 다른 사람들과의 코드리뷰를 진행하면서 함수형 인터페이스
를 활용한 방법이었다.
음 아무리 봐도 획기적인 방법 같다
편의점 미션에서 적용을 해봤는데 함수를 인자로 넣어 재입력 로직의 중복을 확 줄일 수 있었다!
반환값이 없어 멤버변수가 조금 생기긴 한 것 같은데 설계의 부족이라는 생각이 든다
public void run() {
process(this::setupStore);
boolean continuePurchase;
do {
process(this::displayMenu);
process(this::handlePurchase);
process(this::handleMembership);
process(this::printReceipt);
continuePurchase = shouldContinuePurchase();
} while (continuePurchase);
}
저 do-while
도 없애고 싶었지만 다른 마땅한 방법이 떠오르지 않았다
아쉽다
벌써 4주가 지났다!
1주차 미션 시작할 때가 엊그제 같은데 지금 이 회고를 쓰는 시점은 끝난지 2주가 된 후이다.
프리코스를 한다고 미뤄둿던 일들을 조금씩 쳐내다가 더 이상은 미루면 안될 것 같아서 이렇게 쓰고 있다.
요즘 성장이 정체되어 있다고 느끼고 있었는데 이렇게 4주 동안 프리코스에 몰입을 하면서 미션을 진행하니 조금은 성장한 것 같고 재밌다.
주변에 개발자를 지망하는 사람들이 꽤 있지만 이렇게 3천여명과 함께 같은 목표를 위해서 달려나가는 일은 흔하지 않다.
이런 기회를 받을 수 있어서 좋은 것 같다. 다양한 사람들을 만날 수 있었고(온라인이었지만) 나보다 대단한 사람들이나 열정이 엄청난 사람들을 많이 볼 수 있어서 나에게도 자극이 된 것 같다
프리코스를 진행하면서 어떻게 하면 더 나은 코드를 만들 코드에 대한 고민을 수도 없이 하며 개발을 하고, 코드 리뷰를 통해 다양한 사람들의 코드를 보며 더욱 성장하고 나의 시야를 넓힐 수 있어서 좋았다.
딱 한가지 아쉬움이 남는데 그건 바로 4주차 미션....
조금만 더 생각을 해보고 시간투자를 했더라면 좋은 코드가 나오지 않았을까 싶다
나오지 않았을까가 아니고 분명히 나왔을 것이다!!!!
아직은 부족한 나의 실력.. 좀 더 공부해서 실력을 키워보자
탈락하더라도 좌절하지 말고 계속 하던 공부를 해봐야겠다!
프리코스를 진행하며 배운 것들을 잊지 말고 열정을 계속 갖고 나아가보자
다들 고생하셨습니당