이번에 독서 스터디를 하게 됐는데,"소프트웨어장인"이라는 책을 읽게 되었습니다. 해당 책을 읽으며 중요하다고 생각한 부분과 저의 생각을 정리해 보겠습니당 :) 나머지 부분도 읽은 후 차근차근 정리해 블로깅 할 예정입니다!


1장: 21세기의 소프트웨어 개발

중요하다고 생각한 내용과 나의 생각

31p

매일 코드를 작성하는 일은 행복이었다. 그떄 이후로 아침에 눈을 뜰 때마다 오늘 할 일이 하고 싶어 기대되는 일만 하기로 스스로와 약속했다.

→ 자신이 하고 싶어 하는 일을 하는 것은 정말 중요하다고 생각합니다.

→ 여기서 생각난 질문이 있는데 여러분은 ‘자신이 잘하는데 재미가 없는 일’과 ‘자신이 잘하는 것 같지는 않지만 재밌는 일’이 있다면 어떤 것을 선택하실 건가요??

 

32p

이제 개발자들은 개발뿐만 아니라 다음과 같은 여러가지를 할 수 있어야한다.

  • 고객과 대화하기
  • 테스트/배포 자동화하기
  • 전체 비즈니스에 영향을 미칠 기술 선정하기
  • 지리적으로 분산된 팀들과 협업하기
  • 고객을 도와 필요한 작업을 정의하기
  • 우선순위 선정하기
  • 진척 상황 보고하기
  • 변경사항과 기대일정 관리하기
  • 잠재 고객 및 파트너에세 제품 소개하기
  • 사전 영업 활동 지원하기
  • 개발 일정과 비용 산출하기
  • 채용 면접하기
  • 아키텍처 설계하기
  • 비기능적 요구사항과 계약 조건 검토하기
  • 사업 목쵸 이해하기
  • 주어진 여건에서 최적의 결정하기
  • 새로운 기술 주시하기
  • 더 나은 업무 방식 찾기
  • 고객에게 가치 있는 상품이 전달되고 있는지 고민하기

소프트웨어 개발자가 소프트웨어 개발 업무만 하면 되던 시정른 지나갔다.

→ 다양한 역량을 갖춘 개발자가 되어야 계속 발전하는 산업 속에서 살아남을 수 있을거라고 생각합니다.

 

 

2장: 애자일

중요하다고 생각한 내용과 나의 생각

38p

애자일은 절자적인 부분과 기술적인 부분 두 종류로 나눌 수 있다.

절차적인 관점에서의 애자일

팀과 조직이 어떻게 구성되고 협업해야 하는지에 대한 것들 규정

  • 회의 방식
  • 구성원 각각의 역할
  • 요구사랑 파악 방법
  • 작업 진척 속도 파악 방법
  • 점진적/반복적으로 일할 때 취하는 방식
  • 진행 상황을 개발팀 밖의 관계자에게 전달하는 방식
  • 비즈니스 피드백 방식

⇒ 팀에 정말로 중요한 것, 비즈니스에 가치가 있는 것에 집중

결론적으로 팀이 올바른 목표를 향해 진행 중인지 확인

기술적인 관점에서의 애자일

개발, 확장, 유지보수, 제품을 출시하면서 겪는 어려움들에 대해 특정한 기술적 관례나 기술 자체를 매우 구체적으로 가이드

ex) TDD, 페어 프로그래밍, 지속적인 통합, 단순한 디자인 원칙 등

결론적으로 목표한 것을 올바르게 실행하고 있는지에 대해 안심할 수 있게

애자일의 의미

  • 민첩하다고 해서 애자일을 실행하고 있는 것은 아니다.
  • 빠르고 짧은 피드백 루프에 대한 것
    • 더 빨리, 더 짧게 피드백 루프를 만들수록 애자일해진다.
  • 애자일은 문제 자체를 해결해주지 않는다 문제를 드러나게 한다.
  • 특정 기능의 상용화 가능성을 일찍 알게 된다면 투자에 대한 위험도 줄일 수 있다.

→ 애자일 방식을 채택한 이유에 대해 정확하게 이해하며 이번에 진행하는 밋업 프로젝트에 어떻게 적용하면 좋을지 생각할 수 있게 도움을 주는 내용이었습니다. 문제를 빠르게 찾아내는 것이 목표에 빠르게 다가가는 것에 큰 도움이 될것이라고 생각합니다.

 

 

40p

그저 계획에 맞춰서 지시 받은 일만 하는 것이 아니아, 비즈니스와 고객 가치 창출에 개발자들이 직접 함여하는 것도 매우 중요하다고 주장한다.

→ 비즈니스 임팩트를 지닌 코드를 작성하는 것이 정말 중요하다고 생각합니다.

41p

기업들은 단순히 특정 분야만이 아닌 다방면에 걸친 저문가를 찾고 있다. 코드를 잘 작성하는 것은 소프트웨어 프로페셔널이 가져야 할 최소한의 요건이다.

42p

애자일의 12가지 법칙

  1. 가치 있는 소프트웨어를 일찍, 지속적으로 전달하여 고객을 만족시키는 것을 최우선으로 한다.
  2. 개발의 막바지 단계이더라도 고객의 요구사항 변경을 환영한다. 애자일 프로세스들은 변화를 활용하여 고객의 경쟁력을 높이는 데 기여한다.
  3. 동작하는 소프트웨어를 몇 주에서 몇 개월 단위로 자주 전달한다. 가능한 한 전달주기를 짧게 한다.
  4. 비즈니스 담당자들은 프로젝트 기간 내내 매일 개발자와 함께 일한다.
  5. 프로젝트는 동기가 부여된 개인들로 구성한다. 그들이 필요로 하는 환경과 자원을 제공하고 프로젝트가 완료될 때까지 믿고 맡긴다.
  6. 개발팀 내에서 정보를 전달하는 가장 효율적이고 효과적인 바업은 얼굴을 마주보고 대화하는 것이다.
  7. 프로젝트 진척도를 가늠하는 가장 기본 요소는 동작하는 소프트웨어다.
  8. 애자일 프로세스들은 지속 가능한 개발을 이끈다. 투자자, 개발자, 사용자들은 일정한 개발 속도를 계속 수용할 수 있어야 한다.
  9. 기술적인 탁월함과 좋은 설게에 대한 지속적인 관심은 기민함을 높인다.
  10. 단순함, 즉 하지 않아도 되는 일은 최대한 하지 않아야 한다.
  11. 최선의 아키텍처, 요구사항, 설계는 스스로 조직화되는 팀에서 나온다.
  12. 개발팀은 정기적으로 일을 어떻게 하는 것이 더 효과적인지 되돌아보고 그에 맞추어 일하는 방삭을 조율하고 바로잡는다.

 

44p - 안타깝게도 ‘애자일’이 과거에 일하던 방식의 그저 새로운 이름이 된 듯 싶다.

45p - 이러한 변화에도 불구하고 좋은 소프트웨어를 빠르게 공급하지 못하고 있는 것이 현실이다.

→ 이번에 진행하는 프로젝트에서 TDD 방식을 도입해 애자일 방식으로 개발 및 프로젝트를 진행할 예정인데, 위와 같이 기본 방식의 그러 새로운 이름이 되지 않게 노력해야 겠다는 생각이 들었습니다.

 

48p

애자일의 모든 절차들에는 기술적 탁월함이 전제되어 있다.

도요타가 성공할 수 있었던 중요한 요인은 절차를 개선하는 것뿐만 아니라 자동차의 품질에 이미 충분한 역량과 그를 뒷받침하는 노력이 있었기 때문이다.

→ 기술적 탁월함을 갖추기 위해 노력해야겠다는 생각이 들었습니다.

 

53p

절차와 결과물 둘 다 중요하다는 점을 강조하기 위함이다.

54p

소프트웨어 장인정신은 여러 기술적 실행 관례를 활용하고 정교하고 솜씨 있게 짠 코드의 중요성을 강조함과 동시에 코딩을 넘어서 고객의 더 많은 부분을 도울 것을 강조한다. → 일을 올바르게 수행하도록 돕는다.

→ 앞으로 이 책을 읽으면서 소프트웨어 장인정신을 품기위해 꾸준히 노력해야 겠다고 생각했습니다.

백엔드와 함께 서로의 코드를 보면서 진행하고자 GitHub Organization에서 작업을 하기로 결정해 Organization을 만들어 프론트엔드 repo와 백엔드 repo를 만들었습니다.

 

해당 과정에서 문제가 생긴게 Organization의 레포지토리를 Vercel에 배포하려면 유료 결제를 해야한다는 점이었습니다. 그래서 이를 해결할 방법을 찾아봤고, 개인 레포지토리와 Organization의 레포지토리를 연결해 무료로 자동 배포할 수 있는 환경을 구축하는 방법을 알아냈습니다. 해당 과정을 정리하면 좋을 것 같다는 생각이 들어서 이렇게 정리하게 되었습니다.

 

1. Organization의 레포지토리를 개인 계정에 fork 합니다.

2. Vercel에 fork한 레포지토리를 연동합니다.

연동하는 방법은 깃허브 계정 연결 후 배포하려고 하는 레포지토리를 선택하면 됩니다.

 

 

3. secret 토큰을 발급 받습니다.

생각보다 Secret 토큰 발급하는 쪽을 찾기 어렵더라고요

Settings -> Developer Settings -> Personal access tokens -> Tokens (classic) -> Generate new token을 클릭하여 secret 토큰을 발급 받을 수 있습니다.

 

스코프는 상단 repo 체크박스 하나만 체크하고 생성하면 됩니다. 

 

그럼 ghp_로 시작하는 토큰이 발급되는 데, 다시 확인 할 수 없기 때문에 기록해 놓아야 한다.

 

 

4. build.sh 파일을 Organization 레포지토리에 생성한 후 아래의 코드를 입력합니다.

  
#!/bin/sh

cd ../

mkdir output

cp -R ./{Organization의 배포하려고 하는 repo 이름}/* ./output

cp -R ./output ./{Organization의 배포하려고 하는 repo 이름}/

 

 

5. Organization 레포지토리에 발급 받은 secret 토큰을 secret 변수로 등록합니다.

Organization repo -> settings -> Secreat and Variables -> Actions에 들어가 secret 변수 등록

 

토큰을 secret 변수로 저장한 후 GitHub 이메일도 secret 변수로 저장해야한다.

 

 

6. 배포하려는 Organization 레포지토리에 GitHub Action 코드를 작성합니다.

최상단 폴더에 .github 폴더를 만들고 workflows 폴더를 만든 후에 햐소 GitHub Action 코드를 작성할 yml 파일을 생성합니다.

 

name: Deploy



on:

  push:

    branches: ['main']



jobs:

  build:

    runs-on: ubuntu-latest

    container: pandoc/latex

    steps:

      - uses: actions/checkout@v2



      - name: Install mustache (to update the date)

        run: apk add ruby && gem install mustache



      - name: creates output

        run: sh ./build.sh



      - name: Pushes to another repository

        id: push_directory

        uses: cpina/github-action-push-to-another-repository@main

        env:

          API_TOKEN_GITHUB: ${{ secrets.AUTO_ACTIONS }}

        with:

          source-directory: 'output'

          destination-github-username: {내 깃허브 계정 이름}

          destination-repository-name: {배포한 내 레포지토리 이름}

          user-email: ${{ secrets.EMAIL }}

          commit-message: ${{ github.event.commits[0].message }}

          target-branch: main



      - name: Test get variable exported by push-to-another-repository

        run: echo $DESTINATION_CLONED_DIRECTORY

 

 

7. 이렇게 설정 한 후 main 브랜치에 develop 브랜치를 Merge할 때 업데이트가 되는지 확인해 보면 됩니다 :)

 

 

 

 

GitHub Organization 프로젝트를 vercel 무료로 연동하기 (+git actions)

github Organization에 속한 프로젝트는 vercel에서 유료(team plan)로 배포됩니다. 팀 프로젝트를 개인 레포로 클론해 무료(personal plan)로 배포하는 방법입니다.

velog.io

 

JavaScript를 더 깊게 공부하고자 "모던 자바스크립트 Deep Dive"를 읽으며 공부하고 있습니다. 그리고 각 파트를 상세히 기록하며 정리할 예정인데 그 전에 기본적인 것들을 전체적으로 정리할 예정입니다. 드루아 시리즈는 각 언어나 개념들의 전체를 훑으며 그 흐름을 이해하는 데 도움이 되고자 작성하는 시리즈입니다. 잘 부탁드립니당 :)

 

해당 글은 코딩앙마의 자바스크립트 강좌를 기반으로 작성되었습니다. 

 

변수

먼저 변수를 선언하는 과정에서 지켜야 하는 것들이 있는데요. 이는 다음과 같습니다.

  1. 변수는 문자, 숫자, $, _만 사용
  2. 첫글자에 숫자는 올 수 없음
  3. 예약어 사용 불가
  4. 상수는 보통 대문자로 작성
  5. 읽기 쉽고, 이해 가능하게 작성

그리고 이러한 변수는 var, let, const 총 3가지를 통해 선언할 수 있습니다. 각각의 차이점을 알기 위해선 호이스팅이란 개념에 대해 이해해야 합니다.

 

호이스팅이란 코드를 실행하기 전에 함수, 변수, 클래스나 import 선언문을 최상단 스코프로 끌어올리는 것처럼 보이는 현상입니다. 이렇게 보이는 이유는 JS는 코드를 실행하기 전에 선언된 변수와 함수 등을 먼저 체크하기 때문입니다.

 

이제 var, let, const의 차이점에 대해 알아봅시다.

 

var

  • 호이스팅 시 변수의 선언과 초기화를 같이 실행 , 그렇기에 var는 선언하기 전에 사용힐 수 있습니다.(undefined로 초기화 되기 때문에 선언 전에 사용하면 undefined을 가져옵니다.)
  • 함수 스코프 : 함수를 기준으로만 어휘적 환경이 설정 (전역변수와 지역변수의 경계 모호)
  • 재할당 재선언 가능

let

  • 호이스팅이 일어나지만 초기화되지는 않습니다.
  • 블록 스코프 : 블록{}을 기준으로 어휘적 환경이 설정 (전역변수와 지역변수의 경계 확실)
  • TDZ(Temporal Death Zone) 적용 : 호이스팅이 됐지만 초기화 전까지는 해당 변수에 접근 불가
  • 위의 이유로 선언하기 전에는 사용 불가
  • 재할당만 가능

const

  • let과 모두 동일하지만 선언, 초기화, 할당이 동시에 일어나기 때문에 
  • 재선언 재할당 불가능 

선언 : 변수를 JS 엔진에 알려줌

초기화 : 선언된 변수에 기본 값을 할당(변수를 메모리에 등록하고 초기값 할당) 즉, 선언 후 첫 번째 값을 할당할 때 발생

할당 : 초기화 이후의 값 변경

 

 

생성자 함수 

JS에서는 객체를 자주 유용하게 사용합니다. 그렇기에 비슷한 객체를 여러개 만들어야 하는 상황이 생기고, 이 때 사용하는 것이 생성자 함수입니다.

객체 선언 방식은 모두 아시죠?

const user = {
	name: '홍길동',
    age: 34,
    nick_name: '동번서번',
}

위와 같은 객체를 여러개 만들어야 할 경우에 생성자 함수를 사용하는 데 보통 첫글자는 대문자로 작성합니다. 예시로 작성한 객체를 만들어낼 수 있는 생성자 함수를 작성해 보겠습니다.

function User(name, age, nick_name) {
	this.name = name;
    this.age = age;
    this.nickName = nick_name;
}

이러한 생성자 함수를 이용해 새로운 객체를 생성할려면 앞에 new를 붙여서 함수에 필요한 파라미터를 작성해야 합니다.

const hong = new User('홍길동', 34, '동번서번');
const zeon = new User('전우치', 27, '어디보자');

 

이러한 생성자 함수가 객체를 생성할 때는 보이지는 않지만, 내부에서 this에 빈 객체를 할당하고 this에 객체를 생성하기 위해 필요한 프로퍼티를 추가합니다. 그리고 this를 반환해 주는 방식으로 생성합니다.

 

그리고 원하는 메소드(프로퍼티의 값이 함수일 경우)를 추가할 수 있고, 생성된 모든 객체는 추가한 메소드를 불러올 수 있습니다. 예제를 보면서 확실하게 이해해 봅시다.

// 생성자 함수 정의
function User(name, age, nick_name) {
	this.name = name;
    this.age = age;
    this.nickName = nick_name;
    this.callName = function(){
    	console.log(this.name);
    }
    this.callNickName = function(){
    	console.log(this.nickName);
    }
}

// 객체 생성하기
const lee = new User('이순신', 77, '사즉생 생즉사');

// 함수 호출
lee.callName(); // '이순신'
lee.callNickName(); // '사즉생 생즉사'

 

 

Computed property

객체를 선언할 때, [] 안에 계산된 프로퍼티를 넣을 수 있습니다. 이를 computed property(계산된 프로퍼티)라고 합니다.

const user = {
	[4 + 5] : 9,
    ["내 이름은" + "00"] : "당동",
}

// user -> {9: 9, 내 이름은00: "당동"}

 

객체 메소드

객체에서 자주 사용하는 메소드들에 대해 알아봅시다.

 

- Object.assign()

객체를 복제하고 병합할 수 있습니다.

// 객체 복제
Object.assign({}, 객체);

// 객체 병합
Object.assign(객체, 객체1, 객체2);

객체 병합에 대한 예제를 보며 확실하게 이해해 봅시다.

 

const user = {
	name: '이름1',
    age: 33,
}
const info1 = {
	nickName: "이름아름",
}
const info2 = {
	gender: 'male',
}

const newUser = Object.assign(user, info1, info2);

// newUser
// {
// 	name: "이름1",
// 	age: 33,
// 	nickName: "이름아름",
// 	gender: "male",
// }

만약에 병합 과정에서 같은 키를 가진 프로퍼티가 있다면 초기값의 객체가 병합하는 객체에 덮어써집니다.

 

- Object.keys()

키를 배열로 반환합니다.

 

- Object.values()

값을 배열로 반환합니다.

 

- Object.entries()

[키, 값]을 가진 배열을 반환합니다.

 

- Object.fromEntries()

배열 형태로 변경된 객체를 다기 객체로 만들어줍니다.

const arr = [
	["키1", "값1"],
    ["키2", "값2"],
    ["키3", "값3"]
]

const arrToobj = Object.fromEntries(arr);

// arrToobj
// {
//	키1 : "값1",
//    키2 : "값2",
//    키3 : "값3",
//  }

 

 

'웹 풀스택 공부 과정 정리 > HTML, CSS, JS' 카테고리의 다른 글

[HTML] 드루아 ver.1.1.0  (0) 2023.08.23

이번 프로젝트는 TDD 방식을 진행하기로 결정했고, 이를 설정하기 위해 개념을 먼저 정리하고자 합니다.

 

TDD란 무엇인가?

TDD는 작성하고자 하는 코드가 어떤 일을 할 것인지 묘사하고 동작을 검증할 수 있는 테스트 코드를 먼저 작성한 후 테스트를 진행하며 개발하는 방법입니다. 이를 도식화하면 다음과 같습니다.

TDD를 통해 코드의 퀄리티를 높일 수 있고, 빠른 피드백을 통해 개발 시간을 단축할 수 있습니다. 또한 문서의 역할을 하는 테스트를 먼저 작성하기 때문에 더 좋능 문서화가 가능합니다.

 

테스트의 종류

테스트는 단위 테스트, 통합 테스트/스냅샷 테스트, e2e 테스트 이렇게 구분할 수 있습니다.

 

단위 테스트

함수를 직접 호출해 원하는 리턴값이 나오는지 확인하는 테스트 입니다. 테스트 단위가 작아 매우 빠르게 테스트를 진행할 수 있고, 어느 부분에 문제가 있는지 단번에 찾을 수 있습니다.

 

통합 테스트/ snapshot 테스트

한 컴포넌트의 UI 및 동작 방식을 테스트합니다. 통합 테스트와 스냅샷 테스트는 살짝의 차이가 있는데요. 하나의 목적을 기준으로 구분된 컴포넌트들이 올바른 동작을 하는지 확인하는 것을 통합테스트라고 하고, 테스트 전 컴포넌트의 스냅샷을 저장해두고 이후 컴포넌트의 마크업 및 스타일이 바뀌지 않는지 확인하는 것을 스냅샷 테스트라고 합니다.

 

컴포넌트 하나당 하나의 스냅샷 테스트를 구현하는 것이 좋다고 하네용

 

e2e 테스트

최상위에서 진행되는 테스트입니다. 제작한 프로젝트 전체의 동작 과정에 오류가 없는지 확인하는 테스트이기 때문에 매우 오래 걸리고 디버깅에는 어려운이 있습니다. 하지만 각 컴포넌트 사이의 문제를 파악하고 해결하는데 용이합니다.

 

TDD를 적용하는 방법

먼저 TDD의 사이클을 다음과 같습니다.

1. 실패하는 테스트 코드를 작성

2. 테스트를 성공하게 하는 코드 구현

3. 리팩토링

 

하지만 이 내용만으론 어떻게 TDD를 진행해야 하는지 감이 잡히지 않습니다. 이를 조금 더 쉽게 파악할 수 있게 도와주는 로버트 C 마틴의 글은 이렇습니다.

 

  • 실패하는 테스트 없이 코드를 넣지 않기 (테스트 코드 작성 전에는 코드 작성 금지)
  • 실패가 생기면 테스트 작성 멈추기 (새로운 테스트를 작성하는 과정에서 지금까지 작성한 테스트 중 실패가 발생한다면 새로운 테스트 작성을 멈추기 해결하기)
  • 실패한 테스트를 넘기면 즉시 코딩 멈추기(테스트 실패 상태에서 코드를 계속 작성하는 것은 금지되고, 실패한 테스트를 해결할 때까지 코딩을 멈추기)
  • 리팩토링 이후 반복
  • 한 사이클은 10~60초

한 사이클을 굉장히 빨리 돌려야 한다는 것에 놀랐습니다. 즉 테스트를 가장 작게 넣어서 하나씩 빠르게 해결하며 개발하는 것이 좋은 TDD 방식 중 하나라는 것이죠.

 

TDD 시작 전 다음 마음가짐을 가지고 시작하는게 좋다고 합니다.

  • 최소 단위 테스트 : 테스트는 가장 작게 넣어야 합니다.
  • 테스트 대상의 고립 : 테스트 대상은 다른 컴포넌트에 의존성이 없어야 합니다.
  • 테스트의 문서 역할 : 다른 문서가 없어도 테스트를 읽으면 우리의 코드를 다른 개발자가 바로 쓸 수 있도록 작성해야 합니다.

이제 적용 예시를 알아보면 좋을것 같습니다.

 

TDD 적용 예시

프로젝트에서 검색 기능을 구현하고 해당 기능을 TDD 방식으로 개발해봅시다.

 

1. 요구사항 분석 및 테스트 케이스 정의

 

요구사항 :

  • 사용자는 검색 바에 검색어를 입력할 수 있어야 합니다.
  • 사용자가 검색어를 입력하면 해당 검색어에 맞는 프로그램 목록이 표시되어야합니다.

테스트 케이스 :

  1. 검색 바가 렌더링 되어야 합니다.
  2. 검색어를 입력할 수 있어야 합니다.
  3. 검색어 입력 시, 프로그램 목록이 필터링 되어야 합니다.

 

 

2. 테스트 코드 작성 (실패 상태)

SearchPage 컴포넌트가 있다 가정하고 테스트를 작성합니다.

// pages/SearchPage/SearchPage.test.tsx

import React from 'react';
import {render, screen, fireEvent} from '@testing-library/react';
import '@testing-library/jest-dom';
import SearchPage from './SearchPage';

describe('SearchPage 테스트', () => {
	test("검색 바가 렌더링되어야 합니다", ()=>{
    	render(<SearchPage/>); // 페이지 렌더링
    	const searchInput - screen.getByPlaceholderText(/search/i) 
        // search가 포함된 placehoder를 가진 요소 불러오기
    	expect(searchInput).toBeInTheDocument(); // 불러온 요소가 존재하는지 확인
    });
    
    test("검색어를 입력할 수 있어야 합니다", () => {
    	render(<SearchPage/>);
    	const searchInput = screen.getByPlaceholderText(/search/i);
    	fireEvent.change(searchInput, { target: { value: 'React' } }); // 검색 바에 React 입력
    	expect(searchInput).toHaveValue('React'); // React 검색 값을 가지고 있는지 확인
    });
    
    test("검색어 입력 시, 프로그램 목록이 필터링되어야 합니다", () => {
    	render(<searchPage/>);
        const searchInput = screen.getByPlaceholderText(/search/i);
        fireEvent.change(searchInput, {target: {value: 'React'}});
        
        // 필터링된 프로그램 목록이 화면에 표시되어야 함
        const filterProgram - screen.getByText(/React Program/i/);
        expect(filterProgram).toBeInTheDocument();
    });
});

 

 

3. 기능 구현 : 테스트가 성공하도록 기능 코드를 작성합니다.

// pages/SearchPage/SearchPage.tsx

import React, { useState } from 'react';
import ProgramList from '../../components/ProgramList/ProgramList';

const SearchPage = () => {
  const [query, setQuery] = useState('');

  const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    setQuery(e.target.value);
  };

  return (
    <div>
      <h1>Program Search</h1>
      <input
        type="text"
        placeholder="Search"
        value={query}
        onChange={handleSearch}
      />
      <ProgramList query={query} />
    </div>
  );
};

export default SearchPage;

프로그램을 보여주는 ProgramList 컴포넌트도 만들어 줍시다.

// components/ProgramList/ProgramList.tsx

import React from 'react';
import { usePrograms } from '../../hooks/usePrograms';

const ProgramList = ({ query }: { query: string }) => {
  const { data: programs, isLoading, error } = usePrograms();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error fetching programs</div>;
  }

  const filteredPrograms = programs.filter((program: any) =>
    program.name.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <ul>
      {filteredPrograms.map((program: any) => (
        <li key={program.id}>{program.name}</li>
      ))}
    </ul>
  );
};

export default ProgramList;

이렇게하여 모든 테스트를 통과하게 만들어 줍니다. jest --watchAll을 통해 코드를 저장할 때마다 테스트할 수 있도록 해놓고 해야합니다.

 

 

4. 리팩토링

코드를 개선하고 중복을 제거하는 리팩토링을 수행합니다.

위의 내용을 생가해봤을때, 검색 입력 처리 로직을 useSearch 커스텀 훅으로 추출할 수 있습니다.

// src/hooks/useSearch.ts
import { useState } from 'react';

export const useSearch = () => {
  const [query, setQuery] = useState('');

  const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    setQuery(e.target.value);
  };

  return { query, handleSearch };
};
// pages/SearchPage/SearchPage.tsx
import React from 'react';
import ProgramList from '../../components/ProgramList/ProgramList';
import { useSearch } from '../../hooks/useSearch';

const SearchPage = () => {
  const { query, handleSearch } = useSearch();

  return (
    <div>
      <h1>Program Search</h1>
      <input
        type="text"
        placeholder="Search"
        value={query}
        onChange={handleSearch}
      />
      <ProgramList query={query} />
    </div>
  );
};

export default SearchPage;

 

이렇게 한 사이클을 돌렸습니다.

 

이후 CI/CD 파이프라인에 테스트를 추가하여 코드 변경 시 자동으로 테스트를 실행하고 오류를 알려줄 수 있도록 하면 더 좋습니다.

 

이를 기반으로 저도 실제 프로젝트에 적용해 볼 예정이고, 그 과정도 추후에 작성하겠습니다. 감사합니다.

 

+ Recent posts