단위 테스트의 세 가지 스타일

단위 테스트는 아래와 같이 세 가지 스타일이 있다. 하나의 테스트에서 하나 이상의 스타일이 사용될 수 있다.

 

1. 출력 기반 테스트

테스트 대상 시스템(SUT)에 입력을 넣고 생성되는 출력을 점검하는 방식이다. 해당 단위 테스트 스타일은 전역 상태가 내부 상태를 변경하지 않는 코드에만 적용되므로 반환 값만 검증하면 된다.

 

출력 기반 테스트는 시스템이 생성하는 출력을 검증한다. 사이드 이펙트가 없고 작업 결과는 호출자에게 반환하는 값 뿐이다.

 

2. 상태 기반 테스트

상태 기반 테스트는 작업이 완료된 후 시스템 상태를 확인한다. 여기서 상태란 SUT나 협력자, 또는 데이터베이스나 파일 시스템과 같은 프로세스 외부 의존성의 상태를 의미한다.

 

다음은 상태 기반 테스트의 예제다. 클라이언트가 Order를 통해 상품을 주문하고 products 컬렉션을 검증한다.

public class Order {
  private List<Product> products = new ArrayList();
  
  public void addProduct(Product product) {
    products.add(product);
  }
  
  public void getProductOfIndex(int index) {
    return products.get(index);
  }
  
  public int getProductsSize() {
    return products.size()
  }
}

---

@Test
public void add_a_product_to_an_order() {
  Product product = new Product("Hand wash");
  Order sut = new Order();
  
  sut.addProduct(product);
  
  assertThat(sut.getProductOfIndex(0)).isEqualTo(product);
  assertThat(sut.getProductsSize).isEqualTo(1);
}

 

 

3. 통신 기반 테스트

이 스타일은 목을 사용해 테스트 대상 시스템과 협력자 간의 통신을 검증한다. 가급적이면 애플리케이션 경계를 넘는 상호 작용을 확인하고 해당 상호 작용의 사이드 이펙트가 외부 환경에 보이는 경우에만 사용하는 것이 권장된다. 외부 환경에서 보이지 않거나 프로세스 내부 상호작용의 경우는 구현 세부 사항에 해당되기 때문에 부적합하다.

 

스타일 비교

  출력 기반 상태 기반 통신 기반
리팩터링 내성을 지키기 위해 필요한 노력 낮음 중간 중간
유지비 낮음 중간 높음

세 스타일 중에 출력 기반 테스트가 가장 좋다. 그 이유는 다음과 같다.

  1. 구현 세부 사항과 거의 결합되지 않기 때문에 리팩터링 내성을 쉽게 유지할 수 있다.
  2. 테스트가 간결하고 프로세스 외부 의존성이 없기 때문에 유지 보수도 쉽다.

그러므로 출력 기반 테스트를 우선적으로 고려하고, 필요한 경우에만 다른 스타일을 사용하자.

 

테스트 대역이란

XUnit Test Patterns의 저자인 Gerard Meszaros가 정의한 용어로, SUT가 의존하는 구성 요소의 대역을 말한다. 실행 결과를 관측(ex. 이메일 발송 메서드의 호출 여부 검증)하거나 구성 요소가 제공하는 입력값을 세팅(ex. 데이터베이스 응답값 세팅)하기 용이하게 때문에 사용한다. 빠른 테스트를 위해 프로세스 외부 의존성을 대역으로 대체하기도 한다.

 

 

스텁 : sut의 동작이 구성 요소가 반환하는 값의 영향을 받는 경우, 해당 값을 간접 입력이라고 부른다. 스텁은 sut가 의존하는 실제 구성 요소를 대체하여, 테스트가 sut의 간접 입력에 대한 제어점을 가지게 한다. 즉, 데이터베이스 데이터와 같이 sut 내부로 들어오는 입력 데이터를 모방하기 위해 사용한다.

 

더미 : null이나 임의의 문자열 같이 하드코딩된 값을 의미한다. sut가 의존하는 구성 요소 중 관심 없는 요소를 더미로 처리할 수 있다.

 

페이크 : sut가 의존하는 실제 구성 요소의 기능을 대체하기 위해 사용한다. 간접 입력이나 간접 출력을 검증하지 않기 때문에, 간단한 방식으로 구현한다. 

 

목 : sut 동작에 다른 시스템이나 애플리케이션 구성 요소에서 인지할 수 있는 작업이 포함된 경우, 해당 작업을 sut의 간접 출력이라고 부른다. 목은 sut가 실행될 때 간접 출력을 확인하기 위해 사용한다. 즉, 이메일 발송과 같이 sut 외부로 나가는 상호작용을 모방하기 위해 사용한다.

 

스파이 : 목과 동일하게 간접 출력을 확인하기 위해 사용하며, 수동으로 작성하기 때문에 직접 작성한 목이라고 부르기도 한다. 

 

목 vs 스텁

테스트 대역은 사실 목과 스텁의 두 가지 유형으로 나눌 수 있다. 보다 심층적으로 알아보자.

테스트 대역의 모든 변형은 목과 스텁의 두 가지 유형으로 나눌 수 있다.

 

목 : 외부로 나가는 상호 작용을 모방하고 검사하는 데 도움이 된다. 이러한 상호 작용은 sut가 상태를 변경하기 위한 의존성을 호출하는 것에 해당한다.

스텁 : 내부로 들어오는 상호 작용을 모방하는 데 도움이 된다. 이러한 상호 작용은 sut가 입력 데이터를 얻기 위한 의존성을 호출하는 것에 해당한다.

 

이메일 발송은 smtp 서버에 사이드 이펙트를 초래하는 상호 작용, 즉 외부로 나가는 상호 작용이다. 목은 이를 모방하는 테스트 대역에 해당한다. 데이터베이스에서 데이터를 검색하는 것은 내부로 들어오는 상호 작용이다. 사이드 이펙트를 일으키지 않으며, 해당 테스트 대역은 스텁이다.

 

다른 사람이 작성한 글에 있는 예제를 같이 살펴보자

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
    @Mock
    private UserRepository userRepository; // 목을 사용해 스텁 생성
    
    @Test
    void test() {
        when(userRepository.findById(anyLong())).thenReturn(new User(1, "Test User")); // 스텁을 사용해 응답값 세팅
        
        User actual = userService.findById(1);
        assertThat(actual.getId()).isEqualTo(1);
        assertThat(actual.getName()).isEqualTo("Test User");
    }
}

 

 

UserRepository 테스트 대역은 사실 목이 아니라 스텁인 것을 알 수 있다. 왜냐하면 내부로 들어오는 상호 작용, 즉 sut(UserService)에 입력 데이터를 제공하는 호출을 모방하기 때문이다. 만약 이메일 발송을 테스트 대역으로 처리했다면 이는 외부로 나가는 상호작용이기 때문에 해당 테스트 대역은 목이다. 무엇을 사용했냐가 아니라 어떻게 사용했냐에 따라 목과 스텁으로 구분되는 게 요점이다.

 

목과 스텁의 리팩터링 내성

앞서 좋은 단위 테스트 4대 요소에서 배운 리팩터링 내성이라는 관점에서 목과 스텁을 바라보자. 목은 이메일 발송과 같이 외부로 나가는 상호작용이다. 이메일 발송은 실제 사용자가 기대하는, 즉 최종 결과물에 속하기 때문에 이를 검증해도 무방하다. 구현 로직을 변경하더라도 이메일 발송이라는 결과는 변하지 않기 때문에 리팩터링 내성에 어긋나지 않는다.

스텁은 어떨까. 데이터베이스에서 데이터를 조회하는 기능이 최종 결과물일까? 그렇지 않다. 스텁은 최종 결과를 산출해내기 위한 중간 단계에 불과하다. 즉, 스텁은 sut가 출력을 생성하도록 입력값을 제공한다. 따라서 우리는 스텁을 검증하면 안 된다. 이는 테스트가 깨지기 쉬운 안티 패턴에 해당된다.

 

잘 생각해보면 스텁 응답값을 세팅하기 위해 내부 구현 메서드를 직접적으로 의존한다는 사실을 발견할 수 있다. 이는 테스트가 리팩터링 내성이 부족하다는 의미이며, 해당 데이터베이스가 내부적으로 사용하는 경우라면 변경 가능성도 크다. 단위 테스트와 통합 테스트 각각의 상황에서 어떻게 해야 할지는 추후에 다루도록 하겠다.

 

 

들어가며

테스트 코드 역시 코드이기 때문에 지속적으로 관리가 필요하다. 따라서 무분별하게 테스트 코드를 양산하는 것은 좋지 않다.

최소한의 유지비로 최대 가치를 뽑아내기 위해서는, 가치 있는 테스트의 식별과 작성이 가능해야 한다. 이번 장에서는 가치 있는 테스트를 식별하는 방법을 학습해 보자!

 

좋은 단위 테스트의 4대 요소

1. 회귀 방지

회귀란 소프트웨어 버그를 말한다. 그러니 회귀 방지란, 구현한 로직이나 외부 라이브러리와 같은 프로젝트 요소들이 문제가 없도록 검증하는 것을 의미한다. 테스트가 많은 코드를 실행할수록 회귀 방지 지표가 극대화된다.

 

2. 리팩터링 내성

리팩터링 내성이란, 테스트를 실패로 바꾸지 않으면서 기존 로직을 리팩터링 할 수 있는지에 대한 척도이다. 기능을 이전과 동일하게 동작하도록 수정을 했지만 테스트가 실패하는 것을 거짓 양성이라고 부른다. 이런 거짓 양성이 빈번하다면, 테스트 코드에 대한 신뢰도 저하와 그로 인해 리팩터링을 꺼리게 되는 부작용을 낳는다.

 

이를 피하기 위해서는 테스트가 구현 세부 사항과 결합되지 않도록 주의해야 한다. 예를 들어 어떤 사람이 마트에서 과자를 샀다고 생각해 보자. 리팩터링 내성이 좋은 테스트에서는 과자의 재고가 감소했는지만 검증한다. 어떤 방식(또는 알고리즘)으로 재고가 감소했는지, 재고를 감소하는 메서드가 호출 됐는지는 검증하지 않는다. 이는 구현 세부 사항이다.

 

즉, 리팩터링 내성을 높이는 방법은 테스트의 최종 결과를 목표로 하는 것이다. 이는 곧 테스트를 작성할 때는 블랙박스 테스트가 더 낫다는 것을 의미한다. 유일한 예외는 알고리즘 복잡도가 높은 유틸리티 코드를 다루는 경우다.

 

3. 빠른 피드백

테스트 속도가 빠를수록 테스트 스위트에서 더 많은 테스트를 수행할 수 있고 더 자주 실행할 수 있다. 이는 단위 테스트의 필수 속성이다.

 

4. 유지 보수성

유지비를 평가하는 지표로, 다음 두 가지의 주요 요소로 구성된다.

  • 테스트가 얼마나 이해하기 어려운가 : 테스트는 코드 라인이 적을수록 더 읽기 쉽다. 그렇다고 인위적으로 압축하라는 의미는 아니다.
  • 테스트가 얼마나 실행하기 어려운가 : 테스트가 외부 의존성을 가지고 있을 수 있다. 그런 테스트를 실행하려면 데이터베이스 서버를 재부팅하고 네트워크 연결 문제를 해결하는 등의 절차가 필요하다.

 

이상적인 테스트?

위의 네 가지 특성을 모두 만족시키는 이상적인 테스트는 존재하지 않는다. 왜냐하면 처음 세 가지 특성인 회귀 방지, 리팩터링 내성, 빠른 피드백은 상호 배타적이기 때문이다.(유지 보수성은 독립적으로 극대화할 수 있다!) 여기서 리팩터링 내성은 있거나 없거나 둘 중 하나이기 때문에, 회귀 방지와 빠른 피드백 사이에서 절충해야 한다.

 

단위 테스트를 구성하는 방법

테스트를 준비, 실행, 검증이라는 세 부분으로 나눌 수 있다,

  • 준비(given) : 테스트 대상 시스템(sut, 테스트할 클래스를 의미한다)과 해당 의존성을 원하는 상태로 만든다. 
  • 실행(when) : sut에 준비된 의존성을 전달하고 메서드를 호출하며, 출력이 있으면 출력 값을 캡쳐한다.
  • 검증(then) : 결과를 검증한다. 결과는 반환 값이나 sut와 협력자의 최종 상태, sut가 협력자에 호출한 메서드 등으로 표시될 수 있다.
    • 협력자는 공유하거나 변경 가능한 의존성이다. 값이나 불변 객체는 협력자에 해당하지 않는다.

 

아래와 같은 안티패턴은 주의한다.

  • 여러 개의 준비, 실행, 검증 구절 피하기
  • 테스트 내 if 문 피하기 : 유지보수와 가독성이 어려워진다.

 

테스트 간 중복 코드 제거

  • 준비 구절에서 작성하는 객체 생성 로직은 테스트 간에 반복되는 경향이 있다. 해당 로직을 팩토리 메서드로 추출하면, 테스트 간에 재사용이 가능하다. 이때, 테스트 간에 결합이 되지 않도록 주의한다.
  • 데이터베이스와 작동하는 통합테스트와 같이, 테스트 전부 또는 대부분에 사용되는 로직을 abstract 클래스로 추출해서 재사용할 수 도 있다.

단위 테스트

단위 테스트란 1) 작은 코드 조각을 검증하고, 2) 빠르게 수행하고, 3) 격리된 방식으로 처리하는 자동화된 테스트다. 여기서 작은 코드 조각과 격리된 방식을 어떻게 정의하느냐에 따라 런던파와 고전파로 나뉜다. 

 

가령 `친구가 공을 던진다` 라는 행위를 각 분파의 방식으로 테스트 해보겠다. 런던파는 단일 클래스만을 검증한다. 따라서 공을 목으로 처리하고 친구라는 클래스만 검증한다. 반면, 고전파는 단일 동작을 검증하기 때문에, 친구와 공 모두 실제 클래스를 사용하여 행위를 검증한다. 

 

  격리 주체 단위의 크기 테스트 대역 사용 대상
런던파 단위 단일 클래스 불변 의존성 외 모든 의존성
고전파 단위 테스트 단일 클래스 또는 클래스 세트 공유 의존성

 

위 표에 나와 있듯이 격리 방식에도 차이가 있다. 런던파의 격리 주체는 단일 클래스이기 때문에 친구와 공이 서로 격리되어 있다. 하지만 고전파는 클래스가 아니라 테스트 단위로 격리한다. 성공과 실패를 각각 테스트 한다고 하면, 두 테스트는 서로 간섭하지 말아야 한다는 의미이다. 그러기 위해서는 데이터베이스나 싱글톤 같은 요소가 서로 공유되지 말아야 한다.

 

필자는 고전파 방식이 리팩터링 내성과 테스트 유효성이 높다고 판단하였기 때문에 해당 분파를 지지한다.

 

통합 테스트

통합 테스트는 단위 테스트 정의 중 하나를 충족하지 않는 테스트다. 데이터베이스와 같은 공유 의존성에 접근하거나, 둘 이상의 동작 단위를 테스트하는 것이 이에 해당된다.

 

엔드 투 엔드 테스트

엔드 투 엔드 테스트는 통합 테스트의 일부로, 일반적으로 외부 의존성을 더 많이 포함한다. 보통 통합 테스트는 프로세스 외부 의존성을 한두 개만 갖고 동작한다. 반면에 엔드 투 엔드 테스트는 프로세스 외부 의존성을 전부 또는 대다수 갖고 작동한다. 즉, 모든 외부 애플리케이션을 포함해 시스템을 최종 사용자의 관점에서 검증하는 것을 말한다. 동의어로 UI 테스트, GUI 테스트, 기능 테스트가 있다.

들어가며

테스트 코드가 중요하다고는 하지만 막상 일정에 급급하다 보면 테스트를 생략하기 부지기수다. 

이번 장에서는 테스트 코드의 중요성을 학습하고, 오히려 일정 단축에 도움이 된다는 사실을 알아보자!

 

테스트 코드를 작성해야 하는 이유

1. 작성한 코드의 검증

일정상 기능 개발에 급급해서 검증이 안된 api를 내보내는 경우가 종종 있다. 하지만 외부(클라이언트, qa, 사용자 등)에서 버그를 발견되면 더 큰 대가를 치러야 한다. 치러야 할 대가는 서버 측에만 국한된 것이 아니며, 경험한 바에 따르면 아래와 같다.

 

서버에서 들어가는 비용

  • 문제 인식을 위한 커뮤니케이션 비용
  • 문제 해결 비용
    • 센트리, 키바나 등을 사용한 원인 추적
    • 로컬에서 문제 재현
    • 로직 수정 및 배포
  • 개선된 로직이 야기하는 새로운 에러의 가능성

 

외부(클라이언트, qa, 사용자 등)에서 들어가는 비용

  • 문제 전파를 위한 커뮤니케이션 비용
  • 기능에 대한 신뢰 감소
  • 해결되기 전까지 다음 작업이 블로킹
  • 개선된 기능을 재테스트하는 비용

 

2. 리팩터링의 어려움

테스트 코드가 없으면 검증이 상당히 번거롭기 때문에, 로직을 개선하거나 프로젝트 구조를 변경하기 꺼려진다. 반면, 테스트 코드가 잘 짜여있다면 언제든지 테스트할 수 있기 때문에 코드를 변경하는 데에 망설일 이유가 전혀 없다. 따라서 테스트 코드를 적절하게 작성한다면 리팩터링을 통해 더 나은 코드와 더 나은 구조를 지향할 수 있다.

 

마치며

따라서 테스트 코드를 작성하는 습관을 들이자. 그렇다고 아무렇게나 테스트 코드를 작성하라는 의미는 아니다. 테스트 코드 역시 지속적으로 관리해야 하는 '코드'이기 때문에, 무분별한 테스트 코드의 양산은 오히려 독이 될 수 있음을 명심하자.

입사한 지 얼마 안돼서 새로운 프로젝트를 경험하였다. 비록 아직 서비스를 오픈한 것은 아니지만, 개발의 한 프로세스를 경험하면서 느낀 것이 많기 때문에 글을 작성해두려고 한다. 자잘한 것들까지 포함하면 발가락까지 동원해도 셀 수 없지만 크게 여섯 가지로 추려보았다. 추가로 개발 목표도 구체화해 보았다.

 

느낀 점

1. 엄격한 테스트 코드 작성

테스트 코드의 필요성을 크게 코드의 검증과 리팩터링, 이 두 가지에서 느낄 수 있었다.

1) 작성한 코드의 검증

일정상 기능 개발에만 급급해서 검증이 안된 api를 내보내는 경우가 종종 있다. 하지만 외부 사용자(클라, qa 등)가 버그를 발견하는 경우 더 큰 대가를 치러야 한다는 사실을 알 수 있었다. 치뤄야 할 대가는 서버에만 국한된 것이 아니며, 자세한 내용은 아래와 같다.

 

서버에서 들어가는 비용

  • 커뮤니케이션
  • 후속 조치(센트리, 키바나 등을 사용한 원인 추적, 로컬에서 재현, 로직 수정 및 재배포 등)
  • 개선된 로직이 야기하는 새로운 에러의 가능성
  • ...

외부(클라, qa, 사용자 등)에서 들어가는 비용

  • 커뮤니케이션
  • 해결되기 전까지 다음 작업이 블로킹
  • 재테스트
  • ...

2) 리팩터링의 어려움

테스트 코드가 없으면 검증이 어렵기 때문에, 로직을 개선하거나 프로젝트 구조를 변경하기 꺼려진다. 반면에 로직이 잘 작동하는 것을 언제든지 확인할 수 있다면 코드를 변경하는 데에 망설일 이유가 전혀 없다. 따라서 테스트 코드를 꼼꼼하게 작성한다면, 리팩터링을 통해 더 나은 코드와 더 나은 구조를 지향할 수 있게 된다.

 

 

2. 모니터링 도구의 중요성

코드에 문제가 발생하지 않도록 조기에 차단하는 게 좋다. 하지만 애플리케이션이 작동하고 있다면 이슈를 마주할 순간이 온다. 이를 해결하기 위해서는, 당연하게도 에러 추적과 원인 파악이 가능해야 한다. 이럴 때 도움을 주는 것이 모니터링 도구이다. 이를 활용하면 의도하지 않은 에러나 성능 부족을 쉽게 파악하고 대응할 수 있다.

 

 

3. 칸반 보드의 적극적인 활용

1) 백로그의 적극적인 활용

주기적으로 슬랙에 할 일들을 정리한다. 하지만 이 방법은 해야 할 일을 정리하거나 시간이 들고 종종 빼먹기도 한다. 다시 찾아보기도 어렵다는 단점도 있다. 그러지 않고 백로그를 적극적으로 활용한다면 우리는 효율적으로 남은 작업을 파악할 수 있다. 다만, 참여하는 모두가 주기적으로 관리해야 하는 게 번거로운 점이긴 하다.

 

2) 우선순위의 적극적인 활용

개인적으로 프로젝트를 진행하면서 우선순위를 결정하기 어려웠다. 내용을 전혀 알지 못하는 작업이 많았기 때문이다.

그래서 중요도와 시급도를 고려해서 우선순위를 구분하면 좋을 것 같다. 주어진 시간은 한정되어 있기 때문에 중요하지 않은 작업은 뭉개는 것도 좋은 방법이라고 생각한다.

  • 중요 O, 시급 O
  • 중요 O, 시급 X
  • 중요 X, 시급 O
  • 중요 X, 시급 X

 

 

4. 하나의 브랜치는 하나의 기능만 담기

한 pr에 여러 내용이 담기면 코드를 파악하기 어렵다. 무엇보다 해당 pr에 오류가 있어서 롤백해야 할 때, 여러 기능이 담겨있으면 의도하지 않은 에러가 발생할 수도 있다. 따라서 번거롭더라도 한 브랜치는 하나의 기능만을 담는 것이 바람직하다고 생각했다.

 

 

5. 외부와 맞닿은 영역(api 스펙, 에러 코드 등)을 미리 정의하기

프로젝트 구성 요소들은 크게 두 가지로 분류할 수 있을 것 같다. 내부에서만 사용되는 영역과 외부에 노출되는 영역이다.

내부에서 사용되는 영역의 예시로 내부 로직을 들 수 있다. 해당 영역은 테스트 코드가 잘 작성되어 있다면 얼마든지 로직 개선이 가능하다. 따라서 처음부터 완벽하게 로직을 구현하지 않아도 되고, 그럴 수도 없다.

반면, api와 같이 외부에 노출되는 영역은 변경이 어렵다. 이를 수정하기 위해서는 타 직군과 합의하는 과정이 필요하기 때문이다. 따라서 해당 영역은 사전에 타 직군과 함께 미 정의하는 게 중요하다고 생각한다. 그러면서 api만 뚫어도 될지, 웹 소켓을 사용해야 하는지 등도 조기에 파악할 수 있다.

 

 

6. 내가 필요하다고 생각한 작업을 독단으로 진행하지 않기

작업을 하면서 종종 어떤 기능이 필요하다고 생각이 드는 경우가 있다. 그럴 때는 나보다 이해도가 높은 사람이나 기획자와 대화해 보는 게 좋다고 느꼈다. 그 과정에서 요구사항을 명확하게 할 수도 있고, 사실 필요 없는 기능인 경우도 많았기 때문이다.

 

 

향후 목표

프로젝트하면서 가장 크게 다가왔던 것은 시급하게 구현한 메서드가 야기하는 버그이다. 일정상 테스트 코드를 작성을 생략한 게, 오히려 일정에 악영향을 줄 수 있음을 깨달았다. 에러를 효과적으로 추적하는 것도 정말 중요하지만, 그보다 에러 발생을 줄이는 게 더 효과적이다는 말이다.

 

기존에는 24년까지 백엔드 최고가 되겠다는 목표를 가지고 있었다. 그리고 이번 경험을 통해 목표를 더욱 구체화할 수 있었다.

 

먼저 소프트웨어의 프로세스를 개발, 빌드, 배포 세 단계로 구분한다. 그중 개발과 빌드 단계에서 버그 없고 변경에 유연한 애플리케이션을 지향하는 게 내 목표다. 이를 위해 테스트 주도 개발, 빠른 테스트 속도, 지속적인 리팩토링, 좋은 설계, 빠른 빌드를 추구한다. 배포 단계는 기본적인 개념과 구축 경험만 쌓는다.

'블로그' 카테고리의 다른 글

개발자의 학습 방법 고민하기  (0) 2023.08.09
마음가짐 정비하기  (0) 2023.07.30
신입의 백엔드 로드맵  (0) 2023.05.14

대학교에서 화학공학을 전공하던 시절부터, 나는 책을 완독하는 공부법을 선호했다. n회독 할때마다 지식이 축적되고 강화되는게 느껴졌고, 실제로 좋은 성적도 받았었다. 하지만 it 분야는 기술을 직접 사용하는게 중요하다고 느꼈기 때문에, 키보드를 열심히 두들기는 형태로 학습 방법이 변했다.

취업을 하면서 지식의 바다에 빠져버린 지금, 학습 방법의 개선이 필요함을 느꼈다. 기존의 버릇이 남아서인지 기술을 사용하기 전에 최대한 많은 지식을 축적하려는 경향이 있다. 그에 따른 비효율성은 다음과 같다.

  1. 막상 기술을 사용할 때는 일부의 지식만 활용한다.
  2. 많은 지식들 중 어떤게 중요한 개념인지 알기 어렵다.
  3. 기술을 직접 사용하기 전까지는 지식들이 와닿지 않는다. 그로 인해 흥미가 떨어진다.

 

학습 방법에 대해 고민하면서 여러 블로그와 책을 뒤적였다. 그 중 소프트 스킬이라는 책이 가장 잘 정리된 것 같다! 학습 방법은 아래와 같으며, 나에게 적용하면서 글을 수정할 예정이다.

 

 

학습 방법 : 1 ~ 6 단계는 한 번만, 7 ~ 10단계는 반복해서 실행한다.

  1. 큰 그림을 보라
    • 앞으로 배울 주제에 어떤 내용이 있는지, 범위가 어느 정도 되는지 큰 그림을 보는 단계다.
    • 무언가를 처음 배울 때는 무엇을 배워야 할지조차 모른다. 그래서 큰 그림을 파악하는 것이다.
    • 인터넷 검색을 주된 방법으로 활용하되, 관련 도서가 있다면 서론 정도만 읽어보자.
  2. 범위를 정하라
    • 집중적으로 학습할 영역을 명확히 정하는 단계다. 무한대로 늘어날 수 있는 주제를 좁혀야 한다.
    • 배우는 이유와 쓸 수 있는 시간을 고려하여, 적정한 범위로 주제를 한정한다.
  3. 성공을 정의하라
    • 열심히 노력해서 도달할 성공을, 명확하고 간결하게 한 문장으로 정의하자.
    • 나쁜 기준
      • 자바 기초를 배우겠다.
      • 디지털 카메라로 좋은 사진을 찍겠다.
    • 좋은 기준
      • 자바의 주요 기능을 활용해서 간단한 응용 프로그램을 만들겠다.
      • 디지털 카메라의 모든 기능이 각각 어디에 있는지, 각 기능을 어떤 상황에서 어떤 이유로 쓰는지 익히겠다.
  4. 자료를 찾아라
    • 책, 유튜브, 블로그, 소스코드 등 가능한 많은 자료를 찾는다.
  5. 학습 계획을 세워라
    • 자료는 모았으니, 이것을 참고하여 무엇을 어떤 순서로 배울지 정리한다.
    • 어떤 주제든 학습 순서는 어느 정도 정해져 있기 마련이다. 4단계에서 고른 책을 여러 권 뒤적이며 목차를 살펴보는 것도 좋다.
  6. 자료를 선별하라.
    • 여러 자료 중에서, 설정한 목표를 이루는데 가장 도움이 되는 자료만 고른다.
  7. 대충 사용할 수준까지 배워라
    • 우리는 크게 두 가지의 실수를 범한다. 1) 잘 모르는 상태에서 너무 빨리 실전에 뛰어들거나, 2) 너무 오래 준비하느라 행동에 옮길 적절한 시기를 놓친다.
    • 기술을 사용하기 위한 최소한의 내용만 배워라. 파고 들겠다는 유혹을 이겨내야 한다.
  8. 놀아라
    • 아무런 제한 없이 원하는 것은 무엇이든 해보자.
    • 자연스레 궁금증이 생기면, 스스로 고민해보고 자료를 확인하며 질문에 답을 찾자. 답을 찾지 못한 질문을 따로 적어두자.
  9. 유용한 일을 할 정도까지 배워라
    • 8단계에서 답을 찾지 못한 질문의 답을 찾아보자. 또한, 모든 자료를 찾아보면 학습 대상을 깊이 있게 이해하자. 이때가 자료에 파고들어 가능한 많이 배우는 시기다.
    • 모든 자료를 전부 봐야한다는 책임감을 갖지 않아도 된다. 자료는 궁금했던 내용을 찾는 도구에 불과하다.
    • 언제든 8단계로 돌아가서 새로운 질문을 찾아봐도 좋다.
  10. 가르쳐라
    • 배운 내용을 다른 사람에게 설명하는 일은, 자신이 제대로 배웠는지 확인하는 유일한 방법이다. 빠뜨린 부분을 찾아낼 수도 있다.
    • 블로그, 유튜브, 발표, 대화, 질문에 답하기 그 어떤 방법도 좋다.

'블로그' 카테고리의 다른 글

첫 회사 프로젝트에서 느낀 점  (0) 2023.08.15
마음가짐 정비하기  (0) 2023.07.30
신입의 백엔드 로드맵  (0) 2023.05.14

나는 큰 부와 큰 성공의 쟁취, 그리고 원하는 것을 하고자 하는 욕망이 있다. 그곳으로 도달하기 위해 여러 험난한 단계(= 목표)를 거쳐야 한다고 생각한다. 재작년 지금쯤, 웹 공부를 시작하면서 세운 목표가 첫 단계였다. 그리고 그 목표를 올해 초에 이루면서 큰 성취를 느꼈다.(관련 링크)

 

입사한 후에는 투자 공부와 개발 공부를 2 ~ 3년간 지속하며, 두마리 토끼를 잡자고 다짐했다. 하지만 투자 공부가 꽤나 재밌기도 했고, 회사일도 많기도 해서 개발 공부를 소홀히 했던 것 같다. 비록 투자와 개발을 병행하기 어렵다는 점을 느꼈지만, 그래도 소기의 성과는 있다. 그동안 투자에 대한 통찰력을 얻을 수 있었고, 이를 기반으로 한 투자 전략도 세울 수 있었다.

 

이제는 개발에 집중하려 한다. 그 편이 리턴값이 더 크다고 생각한다. 그리고 팀 내에서, 회사 내에서 제일 잘하는 사람이 되고자 하는 욕망도 생겼다. 그러므로 나의 두 번째 목표는 2024년 12월 31일까지 최고가 되는 것이다. 이에 대한 구체적인 내용은 다음 글에서 작성하겠다.

'블로그' 카테고리의 다른 글

첫 회사 프로젝트에서 느낀 점  (0) 2023.08.15
개발자의 학습 방법 고민하기  (0) 2023.08.09
신입의 백엔드 로드맵  (0) 2023.05.14

배경

다음의 절차대로 vault token을 변경을 하니 Pod fail 에러가 지속적으로 발생하는 현상이 발생하였다.

  1. vault token 교체 후 빌드
  2. 생성된 이미지를 바탕으로 deploy 실행
  3. 기존 토큰 삭제
  4. 완료시점부터 배치잡 모두 token 403 에러로 실패.(pod fail)

 

원인

prod 환경은 일반적으로 배포할 때마다 버전(태그)이 변경되므로 이미지 pull 정책이 IfNotPresent로 세팅되어있다. 하지만 위의 절차에서는 단순히 토큰만 바꿔서 빌드를 진행했을 뿐, 새로운 버전으로 빌드를 한 것이 아니었다. 

따라서 pod들은 기존의 토큰을 계속 바라보고 있었기 때문에 해당 에러가 발생하게 되었다.

+ Recent posts