[우아한테크코스 7기 프리코스] 3주차 회고록

 3주차를 마무리하며 지난 시간들을 돌아보니 지원서에 작성했던 목표에 조금씩 도달하고 있는 것 같다. 특히 스스로 깊이 있는 고민을 하는 과정을 즐기기 시작했다는 점이 가장 큰 변화다. 매주 미션을 수행할 때마다 설계-구현-피드백-회고의 순환 학습을 따르니, 각 과정에서 나만의 학습 방법이 명확하게 갖춰지는 것 같다. 특히 피드백 과정에서 공통 피드백, 코드 리뷰, 스터디원 피드백을 모두 정리하고, 이를 차주 목표로 설정하니 더 배워야 할 점들이 눈에 명확히 보이기 시작했다. 또한 코드 리뷰에 다시 한 번 답글을 달며 내가 구현한 코드를 다른 사람에게 설명하는 과정을 통해 메타인지를 할 수 있었던 것 같다. 대면 스터디에서는 각자의 코드를 설명하고, 회고록을 공유하며 더욱 즐겁게 프로그래밍에 임하고 있다. 남은 한 주도 순환 학습, 코드 리뷰, 스터디, 관련 서적 공부를 통해, 지금처럼 즐겨보려 한다.

 

1. 2주차 피드백 정리

공통 피드백

  • 관련 함수를 묶어 클래스를 만들고, 객체들이 협력하여 하나의 큰 기능을 수행하도록 하기
  • 클래스와 함수에 대한 단위 테스트를 통해 의도한 대로 정확하게 작동하는 영역을 확보하기
  • REAMD를 프로젝트의 개요를 설명하는 문서로서 활용하기, 마크다운 문법 적용해보기, 죽은 문서가 아닌 살아있는 문서로 활용하기
  • 변수 이름에 자료형 사용하지 않기
  • 한 메서드가 한 가지 기능만 담당하게 하기, 안내 문구 출력/사용자 입력 처리/유효값 검증 등의 작업을 한 함수에 모두 포함하는 대신 이를 각기 다른 함수로 분리하기, 15라인이 넘지 않도록 구현하기
  • 테스트의 장점 중 본인이 가장 공감하는 작성 이유를 작성하기
  • 문제를 작게 나누어 핵심 기능부터 작게 테스트 만들기

 

코드 리뷰

  • 생성자 주입
  • 일급컬렉션
  • 검증 로직은 어느 곳에서 실행해야 할까?
  • 클래스, 메서드 네이밍에 더 시간 들이기
  • 상수화 할 수 있는 매직넘버 꼼꼼히 살펴보기, 네이밍은 정의가 아닌 용도로써 짓기
  • 점 연산자 축약 방향성 지속적으로 생각해보기
  • 파라미터 수는 2개 이하, Input 단에서 문자열 파싱한 후 비즈니스 로직으로 넘겨주기
  • 최신 자바 API 꼼꼼히 살펴보기
  • 검증 메서드가 많을 땐 2개의 검증 클래스로 분리해보기
  • 행의 가장 마지막에 개행 문자 포함하기
  • 탭 문자 사용하지 않기
  • 테스트가 비즈니스 로직에 영향을 주지 않게 하기
  • 하나의 클래스가 담당하는 관심사를 최대한 분리하기
  • String.format 활용하기
  • 입/출력은 Controller에서 호출하기
  • 주 생성자, 보조 생성자 차이 알기
  • 불필요한 this 사용하지 않기

 

 2주차 때는 10명의 리뷰어분들과 100개의 Conversation을 주고 받았다. 단순히 코드 리뷰를 받는데서 그치지 않고, 리뷰에 답글을 달기 시작하니 내가 짠 코드에 대해 다시 돌아볼 수 있다는 것을 깨달았다. 그리고 나에게 부족한 점이 무엇인지 더 명확히 보이기 시작했다.

 

 

 

 따라서 3주차 때는 15명의 리뷰어분들과 더 활발한 리뷰를 나눠보기로 목표를 설정했다.

 


 

2. 3주차 목표

  • 관련 함수를 묶어 클래스를 만들고, 객체들이 협력하여 하나의 큰 기능을 수행하도록 하기
  • 클래스와 함수에 대한 단위 테스트를 통해 의도한 대로 정확하게 작동하는 영역 확보하기
  • 각 클래스의 역할, 계층의 역할 확실히 확립하기
  • 각 기능에 적절해보이는 디자인 패턴 도입하기
  • 근거를 더 확실히 생각하기
  • 검증 테스트뿐만이 아닌 기능 테스트도 구현하기
  • 코드 리뷰 15명 이상 하기

 


 

3. 걸린 시간

  • 2024.10.30(수)
    • 기능 목록 작성 : 38분 56초
  • 2024.10.31(목)
    • 설계 : 19분 14초
    • 구입 금액 입력 받는 기능 : 38분 56초
    • 구입 금액에 해당하는 만큼 로또를 발행하는 기능 : 14분 30초
    • 발행한 로또 수량 및 번호를 출력하는 기능 : 3분 20초
  • 2024.11.04(월)
    • 당첨 번호를 입력 받는 기능 : 19분 14초
    • 보너스 번호를 입력 받는 기능 : 17분 21초
    • 사용자가 구매한 로또 번호와 당첨 번호를 비교하는 기능 : 20분 5초
    • 당첨 내역을 출력하는 기능 : 8분 5초
    • 수익률을 계산하는 기능 : 7분 21초
    • 수익률을 출력하는 기능 : 5분 8초
    • 리팩토링 : 3시간

 

너무 오래 걸린다,, 5시간 안에 구현하는 연습을 정말 많이 해봐야할 것 같다.

 


 

4. 고민한 과정

4-1. 메서드명은 긍정형 or 부정형?

 검증 메서드를 구현하는 과정에서 메서드명을 긍정형으로 지어야할지, 부정형으로 지어야할지 고민이 되었다. 이펙티브 자바에서는 명확한 의도 전달을 위해 메서드명을 직관적으로 짓는 것이 중요하다고 강조한다. 이에 notDivideThousand()를 divideThousand()로 짓고 나니 해당 메서드가 어떤 역할을 하는지 명확히 이해할 수 있었다.

 

  • 부정형
private void notDivideThousand(int input) {
	if (input % MINIMUM_MONEY_SIZE != REMAIN_OF_DIVIDE_MONEY) {
		throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위로 입력해야 합니다.");
	}
}

 

  • 긍정형
private void divideThousand(int input) {
	if (input % MINIMUM_MONEY_SIZE != REMAIN_OF_DIVIDE_MONEY) {
		throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위로 입력해야 합니다.");
	}
}

 

4-2. 기본적인 입력 검증과 비즈니스적 룰 검증 계층 구분

 2주차 미션에서는 검증 클래스를 따로 만들어, 컨트롤러에서 호출하는 방식으로 구현했다. 이는 도메인에서 호출할 시 검증 호출 시점이 달라져 오류가 발생했기 때문이다. 따라서 3주차 때는 입력에 대한 검증은 InputView에서, 비즈니스적 룰검증은 각 Domain에 분리해 구현하니 코드의 책임이 명확하게 나뉘었다. 또한 비즈니스 로직을 변경해야 할 때 입력 검증 코드와 독립적이어서 오류 수정 및 변경이 훨씬 수월했다.

 

  • 입력에 대한 검증
public static int purchaseMoney() {
	while (true) {
		try {
			String money = Console.readLine();
			return Integer.parseInt(money);
		} catch (NumberFormatException e) {
			System.out.println("[ERROR] 구입 금액은 숫자여야 합니다.");
		}
	}
}

 

  • 비즈니스적 로직 검증
public record PurchaseMoney(int money) {
	public PurchaseMoney {
		validate(money);
	}

	private void validate(int money) {
		moreThanThousand(money);
		divideThousand(money);
	}

	private void moreThanThousand(int input) {
		if (input < MINIMUM_MONEY_SIZE) {
			throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 이상이어야 합니다.");
		}
	}

	private void divideThousand(int input) {
		if (input % MINIMUM_MONEY_SIZE != REMAIN_OF_DIVIDE_MONEY) {
			throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위로 입력해야 합니다.");
		}
	}
}

 

4-3. 일급 컬렉션

 2주차 때 받은 리뷰를 바탕으로 일급컬렉션을 도입했다. 이펙티브 자바에 따르면 컬렉션에 추가적인 로직이 필요할 경우, 별도의 클래스로 래핑하는 것이 좋다고 설명한다. 좋은 코드, 나쁜 코드에서도 일급 컬렉션을 활용하여 중복을 최소화하고 컬렉션 관리 로직을 캡슐화하라고 한다. 따라서 'LottoBundle' 클래스 내에 일급컬렉션을 사용해 로또 번호를 관리할 수 있었고, 클래스가 담당하는 책임이 명확해져 코드의 응집도가 높아졌다.

 

  • Lotto.java
더보기
public class Lotto {
	private final List<Integer> numbers;

	public Lotto(List<Integer> numbers) {
		validate(numbers);
		this.numbers = numbers;
	}

	private void validate(List<Integer> numbers) {
		checkSizeSix(numbers);
		duplicateEach(numbers);
	}

	private void checkSizeSix(List<Integer> numbers) {
		if (numbers.size() != 6) {
			throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다.");
		}
	}

	private void duplicateEach(List<Integer> numbers) {
		Set<Integer> filteredNumbers = new HashSet<>(numbers);
		if (filteredNumbers.size() != numbers.size()) {
			throw new IllegalArgumentException("[ERROR] 로또 번호는 중복되지 않아야 합니다.");
		}
	}

	public static Lotto of(List<Integer> numbers) {
		return new Lotto(numbers);
	}

	public List<Integer> getNumbers() {
		return numbers;
	}
}



 

  • LottoBundle.java
더보기
public record LottoBundle(List<Lotto> lottoBundle) {
	public static LottoBundle of(List<Lotto> lottoBundle) {
		return new LottoBundle(lottoBundle);
	}
}

 

4-4. DTO 도입

 WinningNumber와 BonusNumber를 Winning 클래스 내부에서 선언하니, 변수와 파라미터의 개수가 많아졌다. 또한 클래스 간 의존도가 높아져 DTO로 이를 해결하고자 했다. 하지만 간과한 점이 있었다. DTO를 변수로 선언하는 것은 DTO의 성격에 적절하지 않다는 피드백을 받았다.  WinningNumber와 BonusNumber를 DTO로 변환하는 과정에서 이를 그대로 남겨둔 것이 문제였다,, 리팩토링 과정에서 기능상 문제가 없는지 한번 더 확인하는 습관을 가져야할 것 같다.

 

  • WinningDTO.java
더보기
public record WinningDTO(WinningNumber winningNumber, BonusNumber bonusNumber) {
	public List<Integer> getWinningNumber() {
		return winningNumber.winningNumber();
	}

	public int getBonusNumber() {
		return bonusNumber.getBonusNumber();
	}
}

 

  • Winning.java
더보기
package lotto.model.domain;

import lotto.model.dto.WinningDTO;

import java.util.Arrays;
import java.util.EnumMap;
import java.util.Map;
import java.util.stream.Collectors;

public class Winning {
	private final LottoBundle lottoBundle;
	private final WinningDTO winningDTO;
	private Map<Rank, Integer> rankCounts;

	public Winning(LottoBundle lottoBundle, WinningDTO winningDTO) {
		this.lottoBundle = lottoBundle;
		this.winningDTO = winningDTO;
		rankCounts = new EnumMap<>(Rank.class);
		getWinningResult();
	}

	private void getWinningResult() {
		initializeRankCounts();
		calculateRanks();
	}

	private void initializeRankCounts() {
		rankCounts = Arrays.stream(Rank.values())
			.collect(Collectors.toMap(rank -> rank, rank -> 0));
	}

	private void calculateRanks() {
		lottoBundle.lottoBundle().forEach(this::updateRankCount);
	}

	private void updateRankCount(Lotto lotto) {
		int matchCount = countMatch(lotto);
		Rank rank = Rank.getRank(matchCount, isBonusMatched(lotto));
		incrementRankCount(rank);
	}

	private int countMatch(Lotto lotto) {
		return (int)lotto.getNumbers().stream()
			.filter(winningDTO.getWinningNumber()::contains)
			.count();
	}

	private boolean isBonusMatched(Lotto lotto) {
		return lotto.getNumbers().contains(winningDTO.getBonusNumber());
	}

	private void incrementRankCount(Rank rank) {
		if (rank != null) {
			rankCounts.put(rank, rankCounts.get(rank) + 1);
		}
	}

	public Map<Rank, Integer> getRankCounts() {
		return rankCounts;
	}

	public int getTotalPrize() {
		return calculateTotalPrize();
	}

	private int calculateTotalPrize() {
		return rankCounts.entrySet().stream()
			.mapToInt(entry -> entry.getKey().getPrize() * entry.getValue())
			.sum();
	}
}

 

  • LottoService.java
더보기
public class LottoService {
	public PurchaseMoney createPurchaseMoney(int purchaseAmount) {
		return new PurchaseMoney(purchaseAmount);
	}

	public LottoBundle createLottoBundle(int count) {
		List<Lotto> lottoList = IntStream.range(MIN_RANGE_COUNT, count)
			.mapToObj(i -> LottoFactory.of())
			.collect(Collectors.toList());

		return LottoBundle.of(lottoList);
	}

	public WinningDTO createWinningDTO(WinningNumber winningNumber, BonusNumber bonusNumber) {
		return new WinningDTO(winningNumber, bonusNumber);
	}

	public WinningNumber createWinningNumber(List<Integer> winningNumbers) {
		return new WinningNumber(winningNumbers);
	}

	public BonusNumber createBonusNumber(WinningNumber winningNumber, int bonusNumber) {
		return new BonusNumber(winningNumber.winningNumber(), bonusNumber);
	}

	public Winning checkWinningNumber(LottoBundle lottoBundle, WinningDTO winningDTO) {
		return new Winning(lottoBundle, winningDTO);
	}
}

 

4-5. 다양한 디자인 패턴 도입

- MVC 패턴

Controller, Service, Domain, Util, Factory, View 계층으로 로직을 분리했다. 실제로 도메인 내 비즈니스 로직이 변경되었을 때, 컨트롤러와 뷰 코드에는 수정이 필요하지 않아 편리했다.

 

- 팩토리 패턴 : 정적 팩토리 메서드

이펙티브 자바의 아이템 1에서는 생성자 대신 정적 팩토리 메서드를 활용하는 것을 권장한다. 정적 팩토리 메서드를 사용해 Lotto와 LottoBundle을 생성하니, 외부에서 직접 생성자를 호출하는 대신 필요한 형태로 객체를 만들 수 있어 코드의 일관성과 가독성이 높아졌다. 특히 싱글톤 인스턴스처럼 객체의 유일성을 보장할 수 있었다.

 

  • LottoFactory.java
더보기
public class LottoFactory {
	public static Lotto of() {
		List<Integer> lottoNumbers = Randoms.pickUniqueNumbersInRange(MIN_RANGE_NUMBER, MAX_RANGE_NUMBER, NUMBER_COUNT);
		return Lotto.of(lottoNumbers);
	}
}

 

  • Lotto.java
더보기
public class Lotto {
	private final List<Integer> numbers;

	public Lotto(List<Integer> numbers) {
		validate(numbers);
		this.numbers = numbers;
	}

	private void validate(List<Integer> numbers) {
		checkSizeSix(numbers);
		duplicateEach(numbers);
	}

	private void checkSizeSix(List<Integer> numbers) {
		if (numbers.size() != 6) {
			throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다.");
		}
	}

	private void duplicateEach(List<Integer> numbers) {
		Set<Integer> filteredNumbers = new HashSet<>(numbers);
		if (filteredNumbers.size() != numbers.size()) {
			throw new IllegalArgumentException("[ERROR] 로또 번호는 중복되지 않아야 합니다.");
		}
	}

	public static Lotto of(List<Integer> numbers) {
		return new Lotto(numbers);
	}

	public List<Integer> getNumbers() {
		return numbers;
	}
}

 

  • LottoBundle.java
더보기
public record LottoBundle(List<Lotto> lottoBundle) {
	public static LottoBundle of(List<Lotto> lottoBundle) {
		return new LottoBundle(lottoBundle);
	}
}

 

- 싱글톤 패턴

LottoController를 싱글톤으로 구현하니, 중복 인스턴스가 생기는 것을 방지하고 메모리 사용량을 줄일 수 있었다. 싱글톤 패턴의 이점을 경험할 수 있었지만, 이를 남용하면 오히려 코드의 결합도가 높아질 수 있다는 점을 배웠다. 필요한 곳에만 제한적으로 싱글톤 패턴을 적용해야 하는 이유를 체감했다.

 

  • LottoController.java
더보기
public class LottoController {
	private static final LottoController instance = new LottoController();

	private final LottoService lottoService;
	private final CalculateService calculateService;

	private LottoController() {
		lottoService = new LottoService();
		calculateService = new CalculateService();
	}

	public static LottoController getInstance() {
		return instance;
	}
}

 

  • Application.java
더보기
public class Application {
	public static void main(String[] args) {
		LottoController lottoController = LottoController.getInstance();
		lottoController.start();
	}
}

 

- 전략 패턴 vs Enum 클래스

로또 당첨 순위 관련 로직에 전략 패턴을 적용했을 때는 로직이 너무 복잡해져서 관리하기 어려웠다. 대신 Rank Enum 클래스로 변경하니 코드가 훨씬 간결해지고 직관적이어서 유지보수가 쉬워졌다. 로또 당첨 순위 관련 정보처럼 상태가 고정된 경우에는 Enum이 더 적합하다는 점을 배웠다. 더 나아가 로또에서 2등의 숫자 일치 개수를 가변적으로 다룰 경우, 다른 등수들이 true 값을 가지게 될 경우 등을 고려하며 유연하게 확장하는 법에 대해 깊이 고민해봐야한다는 것을 깨달았다.

 

  • 전략 패턴
더보기
public interface WinningStrategy {
    boolean isWinning(Lotto lotto, WinningNumber winningNumber, int bonusNumber);
}

public class FirstPlaceStrategy implements WinningStrategy {
    @Override
    public boolean isWinning(Lotto lotto, WinningNumber winningNumber, int bonusNumber) {
        return lotto.getMatchingCount(winningNumber.getNumbers()) == 6;
    }
}

public class SecondPlaceStrategy implements WinningStrategy {
    @Override
    public boolean isWinning(Lotto lotto, WinningNumber winningNumber, int bonusNumber) {
        return lotto.getMatchingCount(winningNumber.getNumbers()) == 5 && lotto.contains(bonusNumber);
    }
}

 

  • Rank.java
더보기
public enum Rank {
	FIFTH(3, false, 5_000),
	FOURTH(4, false, 50_000),
	THIRD(5, false, 1_500_000),
	SECOND(5, true, 30_000_000),
	FIRST(6, false, 2_000_000_000),
	;

	private final int matchCount;
	private final boolean requiredBonus;
	private final int prize;

	Rank(int matchCount, boolean requiredBonus, int prize) {
		this.matchCount = matchCount;
		this.requiredBonus = requiredBonus;
		this.prize = prize;
	}

	public static Rank getRank(int matchCount, boolean requiredBonus) {
		return Arrays.stream(Rank.values())
			.filter(rank -> rank.matchCount == matchCount && rank.requiredBonus == requiredBonus)
			.findFirst()
			.orElse(null);
	}

	public int getMatchCount() {
		return matchCount;
	}

	public int getPrize() {
		return prize;
	}
}



 

4-6.  Commit 메시지 수정

커밋 메시지를 명확하게 작성하는 것의 중요성을 한번 더 깨달았다. 특히 git commit --amend를 실수로 작성한 커밋메시지를 수정할 수 있었다. 또한 스터디원분들께 꼼꼼한 커밋 메시지에 대해 좋은 리뷰를 받아, 앞으로도 더 왜, 무엇이 바뀌었는지를 더 명확히 작성해보려 한다.

 

4-7.  record 사용

모던 자바 인 액션에서는 불변 객체의 장점을 강조하며, 불변성은 동시성 문제를 줄이고 코드의 예측 가능성을 높인다고 한다. LottoBundle, PurchaseMoney, WinningNumber 클래스에 record를 적용하니 코드가 훨씬 간결해졌고, 불변 객체가 되는 것을 볼 수 있었다. 이를 통해 상태 변경이 없는 객체에 대해서는 record가 적합하다는 점을 배울 수 있었다.

 

  • LottoBundle.java
더보기
public record LottoBundle(List<Lotto> lottoBundle) {
	public static LottoBundle of(List<Lotto> lottoBundle) {
		return new LottoBundle(lottoBundle);
	}
}

 

  • PurchaseMoney.java
더보기
package lotto.model.domain;

public record PurchaseMoney(int money) {
	private static final int MINIMUM_MONEY_SIZE = 1000;
	private static final int REMAIN_OF_DIVIDE_MONEY = 0;

	public PurchaseMoney {
		validate(money);
	}

	private void validate(int money) {
		moreThanThousand(money);
		divideThousand(money);
	}

	private void moreThanThousand(int input) {
		if (input < MINIMUM_MONEY_SIZE) {
			throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 이상이어야 합니다.");
		}
	}

	private void divideThousand(int input) {
		if (input % MINIMUM_MONEY_SIZE != REMAIN_OF_DIVIDE_MONEY) {
			throw new IllegalArgumentException("[ERROR] 구입 금액은 1,000원 단위로 입력해야 합니다.");
		}
	}

	public int getLottoCount() {
		return money / MINIMUM_MONEY_SIZE;
	}
}

 

  • WinningNumber.java
더보기
package lotto.model.domain;

import static lotto.model.domain.NumberConstant.MIN_RANGE_NUMBER;
import static lotto.model.domain.NumberConstant.MAX_RANGE_NUMBER;
import static lotto.model.domain.NumberConstant.NUMBER_COUNT;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public record WinningNumber(List<Integer> winningNumber) {
	public WinningNumber {
		validate(winningNumber);
	}

	private void validate(List<Integer> winningNumber) {
		checkSizeSix(winningNumber);
		overInRange(winningNumber);
		duplicateEach(winningNumber);
	}

	private void checkSizeSix(List<Integer> winningNumber) {
		if (winningNumber.size() != NUMBER_COUNT) {
			throw new IllegalArgumentException("[ERROR] 당첨 번호는 6개여야 합니다.");
		}
	}

	private void overInRange(List<Integer> winningNumber) {
		if (!winningNumber.stream().allMatch(number -> number >= MIN_RANGE_NUMBER && number <= MAX_RANGE_NUMBER)) {
			throw new IllegalArgumentException("[ERROR] 당첨 번호는 1부터 45 사이여야 합니다.");
		}
	}

	private void duplicateEach(List<Integer> winningNumber) {
		Set<Integer> filteredNumbers = new HashSet<>(winningNumber);
		if (filteredNumbers.size() != winningNumber.size()) {
			throw new IllegalArgumentException("[ERROR] 당첨 번호는 중복되지 않아야 합니다.");
		}
	}

	public static void checkDelimiter(String winningNumber) {
		if (!winningNumber.contains(Delimiter.COMMA.getDelimiter())) {
			throw new IllegalArgumentException("[ERROR] 구분자는 콤마(,)이어야 합니다.");
		}
	}
}

 

4-8. 나에게 테스트코드란?

테스트코드를 작성하는 과정에서 테스트코드는 '안전장치'라는 정의를 내릴 수 있었다. 좋은 코드, 나쁜 코드에서는 테스트 코드를 리팩토링 안전장치로써 활용하라고 한다. 기존 메서드가 예상한 대로 동작하는지 검증하면 코드 변경 시 안전 구역을 제공해 주기 때문에, 리팩토링 과정에서 코드가 잘못될 가능성을 줄이는 데 큰 도움이 되었다. 이에 조금 더 안정감을 갖고 리팩토링을 진행할 수 있었다. 4주차 미션에서는 반드시 TDD를 적용해보고 싶다.

 

테스트코드는 안전장치다
- 좋은 코드, 나쁜 코드 -

 

4-9. collect(Collectors.toList()) vs .toList()

모던 자바 인 액션에서는 .toList()와 같이 변경 불가능한 리스트를 활용하면 코드의 신뢰성을 높일 수 있다고 설명한다. .collect(Collectors.toList())와 .toList()를 비교해보니, 불변성을 보장해야 하는 상황에서는 .toList()가 적합했고, 추가나 삭제가 필요한 리스트에는 .collect(Collectors.toList())가 적합했다. 따라서 불변 리스트를 사용하여 코드의 신뢰성을 높일 수 있었다.

 

5. 4주차 목표

  • TDD 도입하기
  • 내가 구현한 부분도 한번 더 의구심을 가지고 살펴보기
  • 더 깊게 고민하기
  • 더 나은 방향성, 내가 성장할 수 있는 방향성에 대해 끊임없이 고민해보기

 

 벌써 마지막 주차를 앞두고 있다. 프리코스를 시작하기 전과 후를 비교해보았을 때, 부족한 부분들을 채워나가는 과정을 즐기기 시작했다는 점이 가장 큰 변화인 것 같다. 특히 코드 리뷰와 스터디는 매 주마다 항상 기다려지는 시간이다. 함께 성장하는 기분이 들어 이 시간들이 더욱 즐거운 것 같다. 프리코스에서 열정적인 지원자분들과 스터디원분들을 만나 좋다!!!

 

 

3주차 미션 PR 링크

https://github.com/woowacourse-precourse/java-lotto-7/pull/708

 

[로또] 이지현 미션 제출합니다. by Jihyun3478 · Pull Request #708 · woowacourse-precourse/java-lotto-7

로또 프로젝트 목차 프로젝트 소개 프로젝트 주요 기능 목록 프로젝트 아키텍처 입력 예시 테스트 커버리지 리포트 요구사항 정정사항 1. 프로젝트 소개 로또 프로젝트에서는 사용자가 원하는

github.com

 

[참고 자료]

모던 자바 인 액션

 

이펙티브 자바

 

좋은 코드, 나쁜 코드

 

 

https://tecoble.techcourse.co.kr/post/2020-05-08-First-Class-Collection/

 

일급 컬렉션을 사용하는 이유

일급 컬렉션이란? 본 글은 일급 컬렉션 (First Class Collection)의 소개와 써야할 이유를 참고 했다. 일급 컬렉션이란 단어는 소트웍스 앤솔로지의 객체지향 생활체조 규칙 8. 일급 콜렉션 사용에서 언

tecoble.techcourse.co.kr

 

https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/

 

정적 팩토리 메서드(Static Factory Method)는 왜 사용할까?

tecoble.techcourse.co.kr

 

https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/

 

싱글톤(Singleton) 패턴이란?

이번 글에서는 디자인 패턴의 종류 중 하나인 싱글톤 패턴에 대해 알아보자. 싱글톤 패턴이 무엇인지, 패턴 구현 시 주의할 점은 무엇인지에 대해 알아보는 것만으로도 많은 도움이 될 것이라

tecoble.techcourse.co.kr