[우아한테크코스] 프리코스 3주차 미션 회고 - 로또

2025. 11. 5. 00:59·Various Dev/우아한테크코스

들어가며

이번 3주차 미션은 [로또]였다. 로또를 구매하고 몇 개를 맞았는지와 수익률을 계산해 보여주는 비교적 요구사항이 명확한 문제였다.

이번 주차에서 가장 큰 차이점은 에러 처리 방식이었다. 이전까지는 에러가 발생하면 프로그램을 바로 종료시켰지만, 이번에는 에러 메시지를 출력한 뒤 다시 입력을 받는 방식으로 바뀌었다.
이 부분에서의 구현 고민이 특히 많았고, 여기에 더해 함수는 15라인 이내로 제한, 단위 테스트 필수라는 추가 요구사항도 있었다.

이번 글에서는 로또 문제를 풀며 새롭게 도전한 점과 지난 주차 피드백을 어떻게 적용했는지를 정리해보려 한다. 😁  가보자 🔥

https://github.com/woowacourse-precourse/java-lotto-8/pull/236

 

[로또] 노창준 미션 제출합니다. by geniusjun · Pull Request #236 · woowacourse-precourse/java-lotto-8

🧑‍💻 패키지 구조 src └─ main └─ java └─ lotto ├─ Application.java │ ├─ global │ ├─ constants │ │ ├─ ErrorMessage.java │ │ ├─ MessageTyp...

github.com

 

본론으로

🧑‍💻 프로젝트 구현 과정

⚠️ 문제 개요

문제의 진행 과정은 아래와 같다.

  • 사용자가 입력한 금액만큼 로또를 발행한다.(1000원 단위)
  • 로또의 당첨 번호와 보너스 번호를 입력한다. (1 ~ 45까지의 중복되지 않는 6개의 숫자와 보너스 번호 1개)
  • 발행한 로또의 개수와 번호를 정렬하여 출력한다.
  • 당첨 내역과 수익률을 출력한다. (수익률은 둘째 자리에서 반올림)
  • 예외 상황이 발생하면 [ERROR]로 시작하는 에러 문구를 출력한다.

🛠️ 기능 구현 목록

# 기능 구현 목록

### 로또 구입 금액의 입력에 맞게 로또를 발행하는 기능

- [x] 로또 구입 금액 입력 요청 메시지를 출력한다.
- [x] 로또 구입 금액을 입력한다.
    - [x] 빈 문자열인지 검증한다.
    - [x] 숫자로 구성된 입력인지 검증한다.
    - [x] 1000원으로 나누어 떨어지는 금액인지 검증한다.
- [x] 발행할 로또 수량을 계산하여 구매 메시지를 출력한다.
- [x] 수량에 맞게 로또를 정렬하여 발행한다.
    - [x] 6개의 숫자를 랜덤으로 생성한다.
    - [x] 각 숫자의 범위는 1부터 45까지이다.
    - [x] 하나의 로또의 숫자는 중복되지 않는다.
- [x] 발행한 로또를 출력한다.
    - [x] 로또의 수량을 출력한다.
    - [x] 수량에 맞게 로또의 숫자들을 출력한다.

### 최종 당첨 번호를 입력하는 기능

- [x] 당첨 번호 입력 요청 메시지를 출력한다.
- [x] 당첨 번호를 입력한다.
    - [x] 빈 문자열인지 검증한다.
    - [x] 올바른 쉼표로 구성된 입력인지 검증한다.
    - [x] 1~45까지의 입력인지 검증한다.
    - [x] 중복되지 않은 번호인지 검증한다.
- [x] 보너스 번호 입력 요청 메시지를 출력한다.
- [x] 보너스 번호를 입력한다.
    - [x] 빈 문자열인지 검증한다.
    - [x] 1~45까지의 입력인지 검증한다.
    - [x] 당첨번호와 중복되지 않은 번호인지 검증한다.
- [x] 최종 당첨 번호를 만든다.

### 당첨 내역과 수익률 출력하는 기능

- [x] 발행된 로또들과 당첨 번호, 보너스 번호를 비교한다.
    - [x] 발행된 로또 하나와 일치하는 개수를 확인한다.
    - [x] 일치하는 개수에 맞추어 당첨 내역을 업데이트한다.
- [x] 당첨 내역을 통해 수익률을 계산한다.
    - [x] 당첨된 금액에서 로또 구입 금액을 소수점 둘째 자리에서 나눈다.
    - [x] 백분율로 계산한다.
- [x] 당첨 내역과 수익률을 출력한다.
    - [x] '당첨 내역' 메시지를 출력한다.
    - [x] 당첨 내역을 출력한다.
    - [x] 총 수익률을 출력한다.

👷 패키지 구조

src
└─ main
   └─ java
      └─ lotto
         ├─ Application.java
         │
         ├─ global
         │  ├─ constants
         │  │  ├─ ErrorMessage.java
         │  │  ├─ MessageType.java
         │  │  └─ NumberType.java
         │  └─ Parser.java
         │
         ├─ view
         │  ├─ format
         │  │  ├─ LottoFormatter.java
         │  │  └─ StatisticsFormatter.java
         │  ├─ ui
         │  │  ├─ InputView.java
         │  │  └─ OutputView.java
         │  └─ util
         │     └─ InputLoop.java
         │
         ├─ application
         │  ├─ LottoService.java
         │  ├─ LottoFactory.java
         │  └─ RandomLottoFactory.java
         │
         ├─ config
         │  └─ AppConfig.java
         │
         ├─ controller
         │  └─ LottoController.java
         │
         └─ domain
            ├─ result
            │  ├─ MatchResult.java      # record Value Object
            │  ├─ WinningResult.java    # 집계/합산 로직
            │  └─ WinningType.java      # 등수 판별 규칙
            │
            ├─ Bonus.java
            ├─ Cost.java
            ├─ Lotto.java
            ├─ Lottos.java
            ├─ Number.java
            └─ WinningNumbers.java

🧠 시스템 흐름도

1. askCost(), buy(), showLottos() 구입 금액을 입력하고 금액에 맞는 로또를 생성하여 출력한다.

 

2. askWinningNumbers() 당첨번호와 보너스번호를 입력받아 로또와 비교할 WinningNumbers를 생성한다.

 

3. WinningResult.of(), printWinningStatistics() 당첨 내역과 수익률을 계산하여 출력한다.


🧐 2주차 공통 피드백에 대한 나의 생각

본격적인 내용에 들어가기 전에, 2주차 공통 피드백 중 이런 문장이 있었다.

테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다.

솔직히 2주차 미션을 진행할 땐, 테스트를 왜 작성해야 하는지 자신 있게 말하진 못했다. 하지만 깨닫게 해준 경험은 리팩터링 과정에서 일어난 해프닝이었다. Car의 전진 로직을 Cars로 옮기며 모든 차가 한 번에 전진하도록 바꿨는데, 원래 Car 안의 전진 메서드를 지우지 않아 모든 차가 두 번씩 전진해버린 것이다 😂 아마 테스트 코드를 작성해놓지 않았더라면 그냥 커밋해버렸을 것이다. 

테스트 코드가 단순히 기능 검증용이 아니라, 리팩터링의 안전망이 되어준다는 것이라고 당당하게 말하고 싶다. 그래서 토론하기 채널에서 팀원들과 질문에 대해서 의견을 나눠보았다.

총 여섯 분의 팀원분들이 의견을 나눠주었다!

특히 가장 인상 깊었던 것은 테스트 코드를 통해 의도를 먼저 표현한다. 라는 말이 인상 깊었다. 이는 미션에 대한 많은 코드리뷰를 진행하면서 느끼는 것인데, 비즈니스 코드가 한번에 잘 이해가 되지 않을때 테스트코드를 본다면 그 의도를 쉽게 파악할 수 있다는 점이었다. 테스트 코드의 수많은 인자를 테스트 해보면 해당 로직을 더 잘 이해할 수 있다고 생각한다.

 

그래서!! 이번에 완벽한 TDD 방법론을 지키지는 못했지만, 예전에는 기능을 다 작성하고 테스트코드를 작성한 반면 이번에는 코드 작성 중간중간 단위테스트를 작성해나가며 구현을 진행하였다!!

 

아래는 3주차 로직 구현 전 테스트 코드 작성이 익숙치 않다고 생각하여, 피드백에서 받은 pdf를 풀어보며 우테코 제공 테스트 클래스 NsTest에 대해서 깊게 공부해본 블로그이다.

https://geniusjun4663.tistory.com/50 

 

[우아한테크코스] 테스트 코드 작성 연습해보기(우테코 제공 라이브러리 적극 활용! NsTest, assertSi

들어가며들어가기전에 3주차 미션 요구사항을 확인하고 왔는데 "단, UI(System.out, System.in, Scanner) 로직은 제외한다."가 추가되었다... 글 다썻는데 확인했다... 😂😂 NsTest를 통해 전체흐름과 입출

geniusjun4663.tistory.com

 

 

공통 피드백에 다음과 같은 질문이 하나 더 있었다.

메서드가 한 가지 기능을 하는지 확인하는 기준을 세운다

피드백 예시에서는 “메서드의 길이는 15줄을 넘지 않는다”라는 형식적인 기준이 제시되었지만,
나는 조금 다르게 접근해보았다. 메서드명이 정말 ‘하나의 일’을 정확히 표현하고 있는가? 그 안의 로직이 이름이 가리키는 일만 수행하는가?
이런 질문을 스스로에게 던지며 메서드를 점검했다.


🧐 지난 주차 피드백 적용 & 도전한 점들 

1. 어떻게 외부 라이브러리에 대한 로직을 따로 분리할까?

저번주차 문제 특성상 자동차가 랜덤값을 얻어와 조건에 따른을 전진을 하기에 나는 자동차 도메인에 랜덤 값을 얻어오는 로직을 넣어주었다. 여기서 문제는 랜덤값 얻어오는 메서드는 우테코에서 제공해주는 외부라이브러리였던 것이다!! 그래서 아래와 같이 다양한 피드백을 받았다.

처음에는 랜덤값 구해오는 로직을 빼야 테스트하는데 용이하겠구나정도 생각했었는데, 도메인이 외부라이브러리에 의존해버려서 "도메인은 순수해야 한다"는 원칙에 어긋나는 코드를 구현했던 것이었다..!

 

이를 위해 application 패키지를 새로 만들고, LottoFactory라는 인터페이스를 정의했다. Service 클래스는 이 인터페이스에 의존하고, 실제 구현체인 RandomLottoFactory를 주입받도록 구성했다. 

즉, DIP(의존성 역전 원칙)을 적용해 “외부 라이브러리에 대한 의존성을 내부로부터 분리”하고, 필요할 경우 다른 구현체로 쉽게 교체할 수 있는 구조로 만들었다.
이로써 도메인 계층에서 우테코 제공 랜덤 함수(외부 라이브러리)를 직접 호출하지 않도록 구조를 변경했다. 도메인의 순수성을 유지하면서도 확장 가능한 설계를 달성할 수 있었다고 생각한다.

public class LottoService {
    private final LottoFactory lottoFactory;
						// 구현체 주입은 밖에서!
    public LottoService(LottoFactory lottoFactory) {
        this.lottoFactory = lottoFactory;
    }

    public Lottos buyLottos(Cost cost) {
        List<Lotto> lottos = new ArrayList<>();
        for (int i = 0; i < cost.getCount(); i++) {
            lottos.add(lottoFactory.create());
        }
        return Lottos.from(lottos);
    }
}

2. 테스트 코드를 좀 더 효율적으로 작성해보자!

아래와 같은 피드백이 왔다. 저 한마디의 피드백에 많은 테스트 코드 중복을 줄일 수 있었다.

그래서 이번 미션에서는 @ParameterizedTest, @ValueSource, @MethodSource를 적극적으로 사용해서 미리 예측할 수 있는 다양한 입력 케이스를 한 번에 검증하려고 했다. 덕분에 테스트가 훨씬 간결해지고, “이 경우도 되나?” 싶은 것들도 쉽게 추가할 수 있었다.

    @DisplayName("정상적으로 Cost 객체를 생성한다.")
    @ParameterizedTest
    @ValueSource(strings = {"1000", "3000", "1000000"})
    void 비용_생성_테스트(String input) {
        // given // when
        Cost cost = Cost.from(input);
        // then
        assertThat(cost).isNotNull();
    }

    @DisplayName("1000원 단위가 아닌 비용을 입력하면 예외가 발생한다.")
    @ParameterizedTest
    @ValueSource(strings = {"1100", "10500", "1000001"})
    void 비용_생성_실패_테스트(String input) {
        // given // when // then
        assertThatThrownBy(() -> {
            Cost.from(input);
        }).isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining(ErrorMessage.INVALID_UNIT_ERROR.getMessage());
    }

3. 매직 넘버에 대한 끝없는 고찰

아래와 같은 피드백이 왔다. 나는 매직 넘버, 매직 스트링에 대한 고민을 정말 많이 했고 통일시키는 방향으로 진행했다...

그래서 내가 내린 결론은 매직 넘버에 의미를 담고, 0이나 -1 같은 경우는 그대로 보여주는 것이 그 무엇보다 큰 의미를 표현해준다고 결론을 내렸고 3주차 코드에는 0 같은 경우는 그대로 하드코딩하였다.

        private static void validateUnit(int value) {
            if (isNotDivisible(value)) {
                throw new IllegalArgumentException(INVALID_UNIT_ERROR.getMessage());
            }
        }

        private static boolean isNotDivisible(int value) {
            return value % COST_UNIT.getValue() != 0;
        }
    }

4. 이 문제에서 굳이 커스텀 예외를 만들어야할까?

1,2 주차 문제에서는 IllegalArgumentException을 상속하는 커스텀예외를 직접 만들어서 요구사항에 없는 PREFIX를 에러메시지 앞에 붙혀두고 정적 팩토리 메서드를 추가하여 error.from() 식으로 생성하게 하였다. 왜 그렇게 했냐라고 물어본다면, 이 방식이 어디선가 보았고 굉장히 깔끔한 느낌을 받아서 나도 그 느낌을 흉내내보고 싶었다라고 밖에 말하지 못할 것 같다. 역시나 아래와 같은 피드백이 왔다.

사실 1주차에서도 에러 발생시키는 로직은 throw라는 방식으로 개발자들이 많이 알고 있는데, 이걸 마음대로 바꾸면 더 가독성이 떨어진다는 피드백이 있었다. 맞다,,, 솔직히 정말 할말이 없었다. 그래서 아래와 같이 자바 개발자라면 바로 알 수 있게끔 에러 던지는 표현을 수정하였다!

        private static void validateUnit(int value) {
            if (isNotDivisible(value)) {
                throw new IllegalArgumentException(INVALID_UNIT_ERROR.getMessage());
            }
        }

 

5. DI 컨테이너를 흉내내보자!

평소에 구현하던 mvc 패키지 이외에 application 레이어를 도입하니, 메인함수가 아래와 같이 지저분한 듯한 느낌을 받았다.

public class Application {
    public static void main(String[] args) {
        OutputView outputView = new OutputView();
        InputView inputView = new InputView();
        RandomLottoFactory lottoFactory = new RandomLottoFactory();
		LottoService lottoService = new LottoService(lottoFactory);
		InputLoop inputLoop = new InputLoop(outputView);

        LottoController lottoController = new LottoController(outputView, inputView, lottoFactory, lottoService, inputLoop);
        lottoController.play();
    }
}

의존성을 주입을 관리해주면 객체가 있다면 프로그램의 진입시점인 Application 클래스를 가볍게 만들 수 있을 것 같았다! 바로 스프링의 DI 컨테이너가 떠올랐다. 컨테이너가 의존성 주입을 관리하며 반환해주는 느낌을 따라해봤다. 싱글톤 컨테이너라고는 알고 있지만, 굳이 그렇게 까지 구현하지는 않았다! AppConfig를 가장 먼저 생성하면 되니깐 말이다.

/**
 * 애플리케이션 객체의 의존 관계를 설정하고 조립하는 DI 컨테이너
 */
public class AppConfig {
    private final OutputView outputView = new OutputView();
    private final InputView inputView = new InputView();
    private final LottoFactory lottoFactory = new RandomLottoFactory();
    private final LottoService lottoService = new LottoService(lottoFactory);
    private final InputLoop inputLoop = new InputLoop(outputView);

    public LottoController lottoController() {
        return new LottoController(outputView, inputView, lottoService, inputLoop);
    }
}


// 덕분에 매우 깨끗해짐
public class Application {
    public static void main(String[] args) {
        LottoController lottoController = new AppConfig().lottoController();
        lottoController.play();
    }
}

6. 함수형 인터페이스를 활용해보자

이번 미션에서 가장 큰 도전은 함수형 인터페이스를 활용한 구조 만들기였다. 입력 과정에서 예외가 발생하면 메시지를 출력하고 다시 입력을 받는 요구사항 때문에, 여러 곳에서 중복된 try-catch 구조가 생겨 코드가 점점 비대해졌다.

이를 해결하기 위해 Supplier<T>를 도입했다.

입력 재시도 자체는 InputLoop에 모아두고, 실제 컨트롤러에서는 () -> Cost.from(inputView.enterMessage())처럼 “무엇을 만들 것인지”만 람다로 넘기도록 했다. 이렇게 하니 컨트롤러는 도메인 생성과 검증에만 집중하고, 예외 발생 시 다시 입력받는 로직은 중복 없이 한 곳에서 처리할 수 있었다.

public class InputLoop {
    private final OutputView out;

    public InputLoop(OutputView out) {
        this.out = out;
    }

    public <T> T ask(String prompt, Supplier<T> attempt) {
        while (true) {
            try {
                out.printlnMessage(prompt);
                return attempt.get();
            } catch (IllegalArgumentException e) {
                out.printlnMessage(e.getMessage());
            }
        }
    }

}

// 컨트롤러에서 아래와 같이 쓰인다!
    private Cost askCost() {
        return loop.ask(COST_REQUEST_MESSAGE.getMessage(),
                () -> Cost.from(inputView.enterMessage()));
    }
    
     private Lotto askWinningLotto() {
        return loop.ask(WINNING_REQUEST_MESSAGE.getMessage(),
                () -> Lotto.from(Parser.stringToNumbers(inputView.enterMessage())));
    }

 

또한 당첨 결과를 결정하는 WinningType ENUM에서도 Predicate<WinningResult>를 활용해 조건식을 직접 람다로 정의하도록 했다.
애초에 switch와 if-else가 미션에서 사용하지 못하는 것이 조건이지만, 썻다면 굉장히 복잡한 로직이 되었을 것 같다. 그 대신 각 ENUM 상수가 자신의 조건을 스스로 표현하게 한 것이다!
이 덕분에 새로운 규칙을 추가하더라도 ENUM 상수 하나만 추가하면 되는 확장 가능한 구조로 발전시킬 수 있었다.

public enum WinningType {
    NONE(0, "", result -> result.matchedCount() < 3),
    FIFTH(5_000, "3개 일치 (5,000원)", exactly(3)),
    FOURTH(50_000, "4개 일치 (50,000원)", exactly(4)),
    THIRD(1_500_000, "5개 일치 (1,500,000원)", result -> result.matchedCount() == 5 && !result.bonusMatched()),
    SECOND(30_000_000, "5개 일치, 보너스 볼 일치 (30,000,000원)", result -> result.matchedCount() == 5 && result.bonusMatched()),
    FIRST(2_000_000_000, "6개 일치 (2,000,000,000원)", exactly(6));

    private final int prizeAmount;
    private final String label;
    private final Predicate<MatchResult> rule;

    WinningType(int prizeAmount, String label, Predicate<MatchResult> rule) {
        this.prizeAmount = prizeAmount;
        this.label = label;
        this.rule = rule;
    }
    ...

아래 블로그에 함수형 인터페이스에 대해 자세히 공부해보았다.

https://geniusjun4663.tistory.com/51

 

[JAVA] 함수형 인터페이스란?(Supplier<T>, Predicate<T>를 적용하게 된 이유를 중점으로)

들어가며우테코 프리코스 3주차 문제를 풀다보니 문제는 어찌저찌 풀겠지만 중복되는 코드가 너무 많았다. 나는 이 부분을 해결하기 위해 함수형 인터페이스를 활용하였다! 중복되는 코드를 줄

geniusjun4663.tistory.com

7. 객체? 값? 무엇이 같은가

로또 번호 중복을 검증할 때 distinct()를 쓰면 스트림이 객체의 동등성(equals) 기준으로 중복을 판단한다. 그런데 기존 상태에서는 Number.valueOf(5)를 두 번 만들어도 서로 다른 인스턴스라서 “다른 객체”로 취급된다. -> 정적 팩토리 메서드 안에서도 결국 new 연산자를 쓰므로! 이 문제를 테스트 코드 덕분에 발견하였다🔥
로또 번호는 “객체가 같으냐”가 아니라 “값이 같으냐”가 중요하므로, 값 기반으로 동등성을 비교하도록 equals()와 hashCode()를 오버라이딩했다. 이렇게 하면 5라는 값을 가진 Number는 모두 같은 번호로 인식되고, distinct()로도 중복 제거가 가능해지게끔 바꾸었다.

// 로또의 숫자 포장 클래스인 Number이다. equals(), hashCode() 오버리이딩 해주었다!
public class Number {
    private int value;

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Number number = (Number) o;
        return value == number.value;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    public static Number valueOf(int value) {
        Validator.validate(value);
        return new Number(value);
    }

}

// 아래는 로또의 검증로직이다.
    private static class Validator {
        private static void validate(List<Number> numbers) {
            validateDuplicate(numbers);
            validateLottoSize(numbers);
        }

        private static void validateDuplicate(List<Number> numbers) {
            if (isDuplicated(numbers)) {
                throw new IllegalArgumentException(NUMBER_DUPLICATE_ERROR.getMessage());
            }
        }

        private static boolean isDuplicated(List<Number> numbers) {
            return getUniqueSize(numbers) != numbers.size();
        }

        private static int getUniqueSize(List<Number> numbers) {
            return (int) numbers
                    .stream()
                    .distinct()
                    .count();
        }

        private static void validateLottoSize(List<Number> numbers) {
            if (numbers.size() != 6) {
                throw new IllegalArgumentException(INVALID_LOTTO_SIZE_ERROR.getMessage());
            }
        }
    }

자잘자잘한 부분까지 말하자면 굉장히 많지만, 크게 이정도로 시도한 것들을 정리해볼 수 있을 것 같다!

마무리하며

이렇게 지난주 피드백을 적용하고, 새롭게 배운 내용을 프로젝트에 녹여내는 과정을 정리해보았다.
솔직히 말하면 쉽지 않았다. 사실 꽤 어려웠다😂
하지만 어느새 프리코스가 3주차에 접어들면서, 프리코스 시작 전의 나와 지금의 나를 비교해보면 코드를 바라보는 시각이 완전히 달라졌음을 느낀다. 이전에는 “동작만 하면 된다”에 가까웠다면, 지금은 “왜 이렇게 설계해야 하는가”를 고민하며 구조적으로 설득력 있는 코드를 만들고자 한다.
우테코가 아니었다면 이렇게 깊게 코드에 대해 생각하고 리뷰할 기회가 있었을까 싶다. 앞으로도 이번에 배운 이 시각을 잃지 않고, 코드를 단순히 작성하는 것이 아니라 설계하고, 고민하며, 성장하는 개발자로 계속 나아가려고 한다. 🚀

 

4~5주차 미션은 "오픈 미션"으로 난이도가 올라간다는 느낌과는 차원이 다르다. 사실 회고록을 쓰는 시점 오픈 미션이 공개되었다. 도전적인 목표를 정해서 남은 2주동안 나만의 미션을 위해 달려나가는 과정을 즐기면 되는 것 같다. 온 머릿속이 걱정반 설렘반인 상황이다. 하지만 이 느낌은 지원서를 제출하고 프리코스를 진행하기 전 그 느낌과 동일하다🔥 이 느낌을 갖고 3주간 도전하며 몰입해왔더니 어느정도 성장을 이뤘다고는 당당하게 말할 수 있다! 주제를 잘 정해서 2주간 잘 나아가보자!! 오픈 미션이 끝났을 때 지금과 같이 성장했음을 느낄 수 있기를 바라며...🔥

'Various Dev > 우아한테크코스' 카테고리의 다른 글

[우아한테크코스] 8기 프리코스 오픈미션 회고  (0) 2025.11.29
[우아한테크코스] 로또 문제 코틀린으로 리팩토링하기(코드리뷰 피드백을 반영하며)  (0) 2025.11.09
[우아한테크코스] 테스트 코드 작성 연습해보기(우테코 제공 라이브러리 적극 활용! NsTest, assertSimpleTest)  (0) 2025.10.30
[우아한테크코스] 프리코스 2주차 문제 회고 - 자동차 경주  (2) 2025.10.28
[우아한테크코스] 프리코스 1주차 문제 회고 - 문자열 덧셈 계산기  (0) 2025.10.19
'Various Dev/우아한테크코스' 카테고리의 다른 글
  • [우아한테크코스] 8기 프리코스 오픈미션 회고
  • [우아한테크코스] 로또 문제 코틀린으로 리팩토링하기(코드리뷰 피드백을 반영하며)
  • [우아한테크코스] 테스트 코드 작성 연습해보기(우테코 제공 라이브러리 적극 활용! NsTest, assertSimpleTest)
  • [우아한테크코스] 프리코스 2주차 문제 회고 - 자동차 경주
노을을
노을을
진인사대천명
  • 노을을
    노을의 개발일기장
    노을을
  • 전체
    오늘
    어제
    • All (61) N
      • Java & Kotlin (16)
      • Spring (3) N
      • Problem Solve (13) N
      • Computer Science (0)
      • Infra (1)
      • DB (2)
      • Various Dev (23)
        • 우아한테크코스 (9)
        • Git&Github (2)
        • Unity (12)
      • Book (1)
      • Writing (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    게임개발
    java
    합격
    우테코
    코테
    오픈미션
    개발
    자바
    8기
    티스토리챌린지
    유니티
    개발자
    코딩테스트
    알고리즘
    github
    코딩
    스프링
    우아한테크코스
    백준
    프리코스
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
노을을
[우아한테크코스] 프리코스 3주차 미션 회고 - 로또
상단으로

티스토리툴바