[우테코 Level 1] 미션1. 자동차 경주

별의개발자커비·2024년 2월 18일
0

우테코 6기

목록 보기
2/22

개요

이번주의 학습 목표는 다음과 같다!

  • JUnit5, AssertJ
  • 단위 테스트
  • 자동차 경주 미션

📚 강의

🔅 자세

나는 완벽한 정답을 찾을거야! 보다는
스스로 작성한 이유를 적을 수 있으면 도전해보고
나중에 리뷰나 크루간의 이야기로 확인과 발전을 할 수 있다!

(예: 테스트를 위해 차의 거리 지정해줘야할 때 이를 위한 생성자를 만든다 vs 테스트를 위한 코드다 라면 테스트를 위한 생성자를 만드는 파로 간다면 그 이유를 잘 준비해두면 된다!)

🔅 에너지 소모를 고려한 테스트

Q. cars.move 메서드를 테스트 하기위해 car.move가 호출되었는지를 테스트해야할까?

과연, C <- B <- A 이렇게 통해서 테스트해야하는 것들을 다 테스트 해야할까?
'반복문 정도만 있어서 실수할 여지가 거의 없다.'와 같은 경우라면 에너지 소모를 고려해 테스트를 하지 않을 수 있다는 의견을 듣고 단박에 설득되어버렸다.

커버리지에 목매지 말고 코드의 복잡도를 확인하고 테스트의 의미가 있는지 확인하기

자세와 관련해서

A방법이 안좋다고 인터넷에서 해도, 하지만 그건 그 사람의 고민 끝의 과정이므로, 나는 써보면서 고민해볼 수 있다.

🔅 로컬 변수에 final 키워드를 붙여주는 이유

강의를 듣다보니 네오가 메서드 내의 로컬 변수에 final을 자연스럽게 붙여주는 것을 발견하고 그 이유에 대해 질문했다가 커비가 그 이유에 대해 알아와서 다음에 알려주세요😀라는 숙제를 받게되었다.

메서드 내의 로컬 변수들에 fianl을 습관적으로 붙여주는 이유는 무엇일까?

우선, 공통 피드백에서도 비슷한 내용이 아래와 같이 있었으나 짧게만 설명되어있었다.

final 키워드를 사용해 값의 변경을 막아라

최근에 등장하는 프로그래밍 언어들은 기본이 불변 값이다. 자바는 final 키워드를 활용해 값의 변경을 막을 수 있다.

예시 1번

// 1번
public class Car {
    private final String name;
    private int position;

    public Car(final String name) {
        this.name = name;
    }

    ...

}

1번의 경우 생성자의 파라미터에 final을 붙여준 것인데 이럴때의 장,단점을 생각해보면

장점

  • 해당 부분에 들어오는 변수는 변경되지 않아야함을 명시적으로 나타낼 수 있음
    • 생성자의 파라미터는 성격상 final이 기본으로 붙어야할 것이고, 만약 final이 붙지 않는다면 변경의 여지가 있는 상황이라는 메시지를 전달할 수 있겠다.
  • 필드가 초기화 되기 전 받아온 변수와 다르게 값을 변경하는 상황을 막을 수 있음.
public Car(final String name) {
	name = "hi";	// X
	this.name = name;
}

단점

  • 코드가 조금 더 길어진다.
  • 모든 곳에 final을 붙여줘야한다.
    • 팀이라면 팀끼리 컨벤션을 맞추는 등의 작업으로 해결가능하다.
    • 인텔리제이 단축키에서 생성 옵션으로 final을 켤 수 있다고도 한다.

예시 2번

// 2번
@Test
public void 경주_영_바퀴() {
    // given
    final RacingService racingService = new RacingService();
    final int numberOfCars = 3;
    final int time = 0;
    final InputView inputView = new InputView(numberOfCars, time);

    // when
    final List<ResultView> views = racingService.race(inputView);

    // then
    assertThat(views).hasSize(0);
}

2번의 경우 테스트 메서드의 given 부분에 세팅할 변수들에 final을 붙여준 것인데 이럴때의 장,단점을 생각해보면

장점

  • 주어진 변수로 정확히 테스트가 되어야하는데 중간에 time++과 같이 변수의 값이 변경되어 테스트가 예상대로 돌아가지 않는 상황을 막을 수 있다.
  • 역시, 해당 변수들은 이 테스트에서 변경되지 않아야한다는 메시지를 명시적으로 전달할 수 있다.

단점

  • 1번의 단점과 동일하다.

네오 코치의 코드

네오가 제시한 코드는 생성자도, 테스트 메서드도 아닌 일반 메서드 내의 로컬 변수였는데 역시 생각해보면,

장점

사실 테스트 메서드의 장점과 동일하게 느껴지는데,

  • 중간에 변수의 값이 변경되거나 재할당되는 것을 막을 수 있다.
  • 해당 변수들은 이 메서드의 동작에서 재할당하지 않아야한다는 메시지를 명시적으로 전달할 수 있다.

단점

  • 아직 내가 익숙하지 않아서 생기는 문제일 수 있는데, 어떤 변수에 final을 붙여야할지 고민하는데 시간이 소요된다?는 점이 있는 것 같다.
  • 초기에는 final의 성격이었지만, 나중에 변경이 필요한 코드로 수정될 때 오류가 발생하고 해당 부분도 함께 고쳐줘야한다.
  • 너무 당연한 이야기지만, 무작위로 붙이다가 잘못 붙이면 오류가 발생할 수 있다.

결론
생성자의 파라미터, 테스트 메서드의 로컬 변수, 일반 메서드의 로컬 변수
final의 붙인 각각 3가지 상황에서의 장,단점이 조금씩 다르지만 비슷한 부분을 공유하고 있다는 것이 느껴진다!

🔅 테스트 픽스처 생성

테스트 픽스처(test fixture)란 테스트를 반복적으로 수행할 수 있게 도와주고 매번 동일한 결과를 얻을 수 있게 도와주는 '기반이 되는 상태나 환경'을 의미한다. 여러 테스트에서 공용으로 사용할 수 있는 테스트 픽스처는 테스트의 인스턴스 변수 혹은 별도의 클래스에 모아 본다.

🔎 알게된 것

🔅 split 기본 기억하기!

오랜만에 자바 코드를 키니 split이 공백을 어떻게 처리하는지 헷갈려서 정리!

  • 공백도 split 해서 넣어줌
String input = "1,2, ,3,,4";
List<String> values = input.split(",); // [1,2, ,3,,4]
  • 구분자 여러개로 split도 가능
String input = "1,2:3/4";
splittedInput = inputWords.split(",|:|/");  // [1,2,3,4]

🔅 정규식 일부

  • (.) : 한글자만 추출
  • (.*) : 아무 길이의 문자열 허용

🔅 이스케이프 문자 입력 오류

//:\n과 같은 입력에 대해서 //\n 사이의 :를 추출하고 싶을 때 1번처럼 정규식을 사용하면 \n을 인식하지 못한다.

그 이유는? \n가 이스케이프 문자에 해당해서!

이스케이프 문자란?
문자열 내에서 특수한 기능을 수행하는 문자

따라서 \n 이렇게 \하나로만 쓰이면 이스케이프 문자로 헷갈릴 수 있기때문에 \역슬래시를 하나 더 붙여준다. 근데 나의 경우는 아래의 코드에서 역슬래시를 하나 더 붙여주는 걸로 해결이 되지 않았다. 역슬래시 3개를 더 붙여주니 해결되었다. 이유는?

  1. \n의 경우 문자열로 인식되게 하기 위해 \를 하나 더 붙여줘야한다 -> \n
  2. 그러면 역슬래시 2개가 되는데, 정규식이기 때문에 인식을 위해(사실 여기가 이해안간다) 각 역슬래시에 \역슬래시를 하나씩 더 붙여줘야해서, 총 \\n 4개의 역슬래시가 되는 것!
// 1
Pattern pattern = Pattern.compile("//(.)\\n(.*)"); 

// 2
Pattern pattern = Pattern.compile("//(.)\\\\n(.*)"); // 역슬래시 3개 + 이스케이프문자

Matcher m = pattern.matcher(input);

String[] splittedInput;

if (m.find()) {
	String delimiter = m.group(1);  
	String input = m.group(2);
	splittedInput = input.split(delimiter); 
}

🔅 PattenSyntaxException

1*2*3라는 입력을 *로 split하려고 했는데 다음과 같은 예외가 발생했다.

Exception in thread "main" java.util.regex.PattenSyntaxException: Dangling meta character -

찾아보니 ?, *, + ,( ,) ,[, ], {, } 와 같은 문자를
replace, replaceAll, split 등을 사용하여 나누거나 대체할 때 생기는 예외였다.

해결방법은 역슬래쉬\ 를 붙여주면 된다!

🔅 isBlank vs isEmpty

평소에는 습관적으로 isBlank를 isNull과 함께 썼는데 둘의 차이 구분해놔야겠다!
isBlank는 " "를 빈것이라고 보고, isEmpty는 빈것이라고 안보는 것!

입력isEmpty()isBlank
nulltruetrue
""truetrue
" "falsetrue
"hi"falsefalse

🔅 리스트끼리 비교 containsAll

리스트끼리 비교할 수 있는 assertJ 메서드인데 비슷한듯 달라서 정리!

containsAll

모든 요소를 포함하고 있으면 OK

containsExactlyElementsOf

요소가 순서부터 아예 똑같아야함

✏️ 이번주의 고민들

🔅 불변 객체

해당 리뷰에 대해 불변객체로 해결할 수 있지 않을까? 하는 생각이 들었다.

distance를 증가시키는 것이 아니라 어차피 distance는 VO, 즉 값객체의 성격이므로 1을 증가시킨 새로운 Distance 객체를 반환해주는 것이다!

장점

  • 객체를 처음 만들었을 때의 속성을 해당 객체가 사라질 때까지 그대로 유지할 수 있고,
  • 만약, car.getDistance.increase()처럼 호출한다해도 원래의 값이 바뀌지 않게하고자 적용해보았고

단점

  • 새로운 객체를 계속 생성해야해서 약간의 성능저하

🔅 VO 값 객체 (vs Dto)

위의 값 객체와 관련되어 일타강사 로키가 설명해주어서 값 객체, 즉 VO(Value Object)에 대해서도 새로 개념을 정립해보았다!

  1. VO는 갖고있는 값이 같으면 같은 객체여야하는 객체인 경우에 쓰는 것이다.
  2. 따라서, Car 객체는 VO가 될 수 없다.
    • 차이름name과 간 거리distance가 같다고 같은 자동차일까? NO!
  3. 하지만, Point(좌표)는 VO가 될 수 있다. 3차원 좌표에서 x, y, z 좌표가 같으면 같은 점이 맞으니까! 이런 성격의 객체는 VO가 될 수 있다!
    • 주소객체에 시, 군, 동이라는 필드가 있다면 이것도 VO가 될 수 있겠지!

🔅 랜덤생성기를 Application에서 주입?

처음에는 습관적으로 MovementGenerator를 Application에서 생성해서 주입해주었는데
이야기를 하다보니, Application이 Movegenerator를 알 필요가 있을까? 하는 생각이 들었다.

만약, Game과 관련된 객체가 아니었다면 Application에서 생성해주어서 넣어주는 것이 맞을 것이다. (InputView처럼). Game과 관련된 객체가 아닌데 그 아래 GameController부터 생성하는 것은 이상하니까.

하지만 MoveGenerator처럼 게임과 밀접한 관련이 있는 객체는 Controller 내에서 생성하는 것이 이상하지 않기때문에 Application에서 생성하는 게 오히려 Application이 알 필요가 없는 것까지 알고있다고 할 수 있을 것 같다!

🔅 Dto

프리코스 때는 안쓰던 Dto를 이번에 쓰면서 많은 고민점들이 있었다

- getter 호출 허용

도메인 객체의 경우 getter를 지양하는 것은 자명하다.
하지만 Dto에서는 허용되는데 왤까?

Dto의 목적을 떠올려보자면,

  • request: 입력(원시값)을 받아서 도메인으로의 전달을 위해 포장해주는 역할
    • 이 경우에는 getter를 사용할 필요가 없고
  • response: 객체를 받아서 view로의 전달을 위해 필요한 데이터만 뽑아서 포장해주는 역할

response의 경우 어차피 view를 위해서는 객체의 getter를 호출해야하는데 그걸 Dto가 해주려면 getter는 똑같이 허용될 수 밖에 없다.

- 객체 변환 메서드는 객체에? DTO에?

  1. 도메인에 fromDto()가 있다면 이때의 문제점은,
  • 만약 view에 변경사항이 생겨서, CarName만이 아니라 CarColor도 받아야한다면 그 변경사항이 Cars 객체 내의 코드에 영향을 준다.
  • 도메인 로직안에 Dto를 위한 코드가 존재한다는 부담감이 생긴다.
  1. Dto에 객체로 변환해주는 toCar()이 있는 경우는, 위같이 domain이 외부에 의존하는 문제는 발생하지 않게된다.

정리하니 명확해졌다! Dto에 넣자!

- 컨트롤러에서 생성해도 될까?

Model, View 레이어간의 데이터 이동이 Dto의 역할이라면
컨트롤러에서 Dto를 생성하는 것보다 이동이 시작되는 Model이나 View에서 생성하는 것이 자연스러운 것인가?라는 이야기를 페어랑 나누게되었다.

그렇다면, 컨트롤러가 Dto를 생성하지 않는다면? Winners 객체 자체에 toDto()라는 메서드를 넣어주는 방법이 있겠으나 객체 부담이 커진다.
즉, 컨트롤러가 그러한 객체의 부담감을 줄여주는 위치로 작용할 수 있다고 결론을 내렸다!

- 검증을 담당해도 될까?

자동차 이름 입력에 해당하는 Dto를 생성하였으니 이름에 대한 형식 검증을 InputView가 아닌 Dto로 옮겨줘도 될까? 하는 생각이 들었다.

이를 생각하려면 Dto의 역할을 다시 떠올려봐야할 것 같다.
Dto의 역할이란? View <-> Model 사이의 데이터 전송을 위해 필요한 데이터만 포장되어 돌아다니는 역할인데, 전송을 위한 역할이 검증을 한다는 것이 자연스럽게 느껴지지 않는다. 현재에는 view, model에서 검증하는 형태를 유지하는 것으로 결정!
(추후 보완)

🔅 테스트만을 위한 코드? 괜찮?

테스트만을 위한 메서드 생성은 지양해야한다고 생각하는데,
Car.of(Name name, Distance distance)와 같이 테스트를 위한 부생성자도 지양해야하는 부분인지 의문이 들었다.

이 코드의 단점이라면,

  1. 모든 코드는 유지보수 대상이고, 유지보수 비용은 낮을수록 좋다. 테스트 코드를 위한 메서드에 대한 유지 보수가 필요하게된다.
  2. 외부에서 보기에 이 메서드가 어떤 운영 로직에 필요한 메서드인지 고민하게 된다.

장점은,

  1. 테스트가 편해진다.
new Car(new Name("a"), new Distance(2)) // 테스트를 위한 부생성자를 만들지 않는 경우
new Car("a", 2) // 테스트를 위한 부생성자를 만드는 경우

하지만 쓰고보니, 장점이 크지 않다는 느낌이 든다.
테스트를 위한 코드... 결국 좋지 않다는 결론을 내리게 됨...

🔅 테스트에서의 함수형 인터페이스 형태 구현

나는 이렇게 구현을 했지만 사용자가 numberGenerator가 함수형 인터페이스라서 () -> 3 형태로 사용할 수 있다는 것을 어떻게 아는가? 라는 질문을 받았고 해결방법을 생각해보았다.

명시적인 이름의 테스트만을 위한 구현체를 사용하는 방식을 크루들의 코드에서 발견했고 적용을 할 수 있는 방법을 보니 2가지 방법이 있을 것 같다.

  • 1번 방법: 테스트 클래스에 이너클래스로 생성

  • 2번 방법: ConstantNumberGenerator 구현체 클래스 생성

1, 2번의 선택은 우선 한 테스트 클래스에서만 필요한 경우일테니 1번 방법으로 구현하고, 추후에 다른 클래스에서도 필요하면 그 때 구현체 클래스를 따로 생성하는 것이 오버 엔지니어링을 하지 않는 방법일 것 같다.

👩‍💻 스스로 답해보기

🔅 1번

Q. 자동차 경주에 단위 테스트를 얼마나 추가하였는가?

검증, 프로그램 실행에 주요한 메서드들에 대한 테스트는 다 추가했다.

Q. 그 과정에서 어려웠던 점은 없는가?

  1. 현재 프로그램 사이즈에서는 인터페이스가 필요하지 않을 것 같은데, 테스트 편의를 위해 인터페이스를 추가한 것이, 테스트만을 위한 인터페이스 구현인데 이것이 괜찮은 일인지 고민이 되었다.
  • 결론: 실행 자체에는 구현체로만 구현해도 되었겠기에 오버 엔지니어링이라고 할 수 있으나 감수할 수 있는 정도라고 결론을 내렸다.
  1. 특정 메서드를 호출하는지 정도를 테스트하고 싶은 경우, 그렇다면 중요한 메서드이긴하지만, 연결 역할정도만 하는 메서드도 테스트 커버가 될 것 같다고 생각했는데, 여기서 테스트에 들이는 에너지 소모를 생각해야한다는 점을 생각하지 못했다는 걸 깨닫게 되었다.
  • 결론: 코드의 복잡도를 확인하고 테스트를 만들정도의 에너지 소모가 필요한지 보고 판단하자.

🔅 2번

Q. 코드 품질을 위해 노력한 부분은 무엇인가?

데이터의 안정성, 즉 변조 위험을 줄이기 위해
1. 데이터가 View와 Model 사이를 Dto로 이동할 수 있게 하였다.
2. final 키워드, 불변객체, unmodifiableList를 활용해 불변성을 적용하고자하였다.

🔅 3번

Q. 해당 미션에서 작성한 본인의 코드가 만족스러운가?

만족스럽다!

  1. 학습 목표인 단위 테스트에 대해 이번주동안 테스트 생성 기준을 정립했고, 그에 따라 필요한 단위 테스트를 작성했기 때문이다.
  2. 책임을 분리한 코드라서. 하나의 메서드가 하나의 기능만 하고, 나아가 객체에서는 하나의 객체가 과중한 책임을 갖지 않게 구현했기 때문이다!
  3. (작성중...)

Q. 다음 미션에선 어떠한 목표로 코드를 작성할 예정인가?

크루들과 가장 많이 나눴던 이야기 중에 하나가 랜덤 생성기를 어떤 객체가 갖고 있어야할까?에 대한 것이었는데 이 이야기로 정말 많은 시간을 보낸 것 같다. 그러다가 문득 한 크루가 우리 이 고민을 하는데 리소스가 엄청 많이 발생하고 있는 거 아니야? 라고 이야기하면서 문득 깨달았다.
사실 나 개인으로 봤을 때 더 심하게 발생하는 부분이기도 한 점인데, (작성중...)

🔅 4번

(강의) 내가 단위 테스트를 작성하는 이유는 무엇일까?

  1. 입력값에 대한 예상 기대값을 정해놓고, 실행 메서드를 작성하기 위해
  2. 메서드가 정상 작동하는지 확인하기 위해서

🔅 5번

(강의) 내가 작성한 단위테스트는 어떤 부분에서 좋은 단위 테스트인가?

  1. 사용자가 랜덤 테스트 사용법을 알 수 있게 이너클래스로 명시적으로 랜덤 생성기를 구현체로 구현했다.
  2. DisplayName을 통해 어떤 테스트인지 파악이 쉽고, 관련해 테스트를 위한 입력값과 기대값이 명확하다.
  3. (작성중...)

✅ TODO

🔅 git 공부해서 포스팅하기

🔅 정규식 공부하기

지금까지 숫자 검증하는데 습관적으로 \\d+ 썼는데 돌아가는 원리를 알아봐야겠다.
Matcher도 함께 공부하기! group(1), m.find 등

🔅 공식 문서 보는 습관

페어 프로그래밍을 하면서 페어가 모르는 게 있을 때 예를 들어 나는 Record의 부작용 이런식으로 구글링을 할 때, 페어는 공식 문서로 확인하는 것을 보고 오호?싶었다.

영어라 술술 안읽혀서 그런 마음 반, 남들이 정리해준 거 빨리 보고 이해하고 넘어가고 싶은 마음 반이었는데 공식 문서를 보는 습관을 어떻게 들였는지 다른 크루들에게도 물어보면서 시도해봐야겠다!.

🔅 extracting

테스트를 위해 getter를 열어 쓰는 경우가 있었는데 이또한 테스트를 위한 코드이고 다른 곳에서 이 getter를 호출할 수 있는 위험성이 열리는 것이므로, 이 테스트를 위한 getter를 대체할 수 있는 방법으로 assertJ의 extracting을 더 활용해봐야겠다.

        /**
         * `extracting` 메서드는 Collection에 포함된 객체들 중 특정 필드를 추출할 때 사용합니다.
         * <p>
         * `extracting` 메서드는 추출한 필드를 기반으로 메서드를 사용할 수 있습니다. 추출한 필드를 기반으로 메서드를 사용하면 테스트 코드를 작성할 때 더 편리하게 작성할 수 있습니다.
         */
        @Test
        @DisplayName("extracting 메서드로 Collection에 포함된 객체들 중 특정 필드를 추출한다")
        void extracting_메서드로_Collection에_포함된_객체들_중_특정_필드를_추출한다() {
            class User {
                private final String username;
                private final String password;

                User(final String username, final String password) {
                    this.username = username;
                    this.password = password;
                }

                public String getUsername() {
                    return username;
                }
            }

            final var actual = List.of(
                    new User("user1", "password1"),
                    new User("user2", "password2"),
                    new User("user3", "password3")
            );
            final var expected = List.of("user1", "user2", "user3");

            // TODO: AssertJ의 extracting 메서드를 사용하여 actual에 포함된 User 객체들 중 username 필드를 추출하여 expected와 비교해보세요.
            for (int i = 0, end = actual.size(); i < end; i++) {
                assertEquals(actual.get(i).getUsername(), expected.get(i));
            }

            assertThat(actual).extracting("username", String.class)
                    .containsAll(expected);

            /* ----- 아래는 추가로 학습할 분만 보세요! -----
            `extracting`을 사용할 경우 getter가 없어도 필드값을 추출할 수 있습니다.
            그 이유는 `extracting` 메서드가 Reflection을 사용하기 때문입니다.
            Reflection은 객체의 필드나 메서드에 접근할 수 있도록 해주는 기능이지만, Reflection을 사용할 때는 주의해야 합니다.

            참고: https://docs.oracle.com/javase%2Ftutorial%2F/reflect/member/fieldValues.html
             */
        }

🔅 reflection

객체의 copy 느낌으로 쓰는 것인데 copy라기에는 딱 맞아떨어지는 개념은 아닌 것 같아 추후에 더 알아봐야겠다.

🔅 재입력 처리의 책임이 컨트롤러가 담당해도 괜찮을지

현재는 컨트롤러에서만 재입력 처리가 이루어지므로 컨트롤러 내에 메서드로 만들어주었는데
예외에 대한 재입력이라는 측면에서 책임 분리가 필요할까?라는 생각이 든다.

🔅 이너 클래스 알아보기

테스트 클래스에 이너클래스로 생성를 하면서 이너 클래스 사용이 익숙하지 않은 것을 느껴서 알아봐야겠다!

🙏 감정 회고

나도 저렇게 될 수 있을까?

오티에서 보여준 첫 문장은 이것이었다.

소프트웨어 교육을 통해 사람들에게 변화를 만든다.

이어진 문장들

공부가 이렇게 재밌을 수 있구나, 일이 재밌을 수 있다를 경험하기를,
평생 학습’을 하려면 학습을 즐겨야 가능한데, 그걸 할 수 있게 하는 곳이다.
100년 개발 인생을 위해 재미있게 개발하는 방법을 배우기!

우테코에 빠르게 적응하는 방법은?
일단 도전하기! 빠르게 실패하기! 그 과정에서 새로운 인사이트를 얻기!
망가져도 괜찮다, 우테코는 그걸 지지한다

성취를 보며 달려온 나에게는 낯선 이야기들이 아닐 수 없었다.
나도 저렇게 바뀔 수 있을까?

신기한 잡담

1주차가 끝났다. 너무 오래쉬었나 페어 프로그래밍을 하는데 바로바로 안떠오르는 나에게 실망하고 당황하기도 하며 지나갔는데,

그 와중에 느낀점 첫째, 우테코에서 잡담이란 뭘까?
6일을 했다고 생각이 드는데, 가장 많이 느꼈던 지점은 일반적인 의미와는 다른 잡담이 정말 활발하게 일어난다는 것이었다.

사실 처음에는 미션 진행하기 바빠서 아침에 잠깐 소파에 모였을 때, 밥 먹을 때 정도 나누는 정도였다면,
하루하루 지날 수록 잡담을 시작하고, 또는 진행중인 잡담에 들어가 이야기하는 것의 즐거움을 느끼고 있다. 더불어 거기에서 무언가가 발생한다는 느낌도!

물론 경계할 필요는 있겠으나, 뭐든 해보고 실패를 경험하면 돌아가는게 우테코니까!

git, 아찔

정말이지 첫 미션 제출은 git과의 사투였다...
페어의 로컬 브랜치를 add remote ~ 하는 부분을 미션 제출 1시간 남기고 진행했는데 어디선가 꼬였는지 안되기 시작했고 20분쯤 남기고 크루들에게 도움을 청하러 돌아다니기에 이르렀다. 다행히도 git 장인 러쉬덕분에 2분남기고 제출할 수 있었는데, 하마터면 6시를 넘길뻔 했다..🥲
시간나면 git 공부 제대로 해봐야겠다는 생각이 활활!

0개의 댓글