-
1/16 프론트엔드 온보딩 인턴십 내용 정리.원티드 프리온보딩 2023. 1. 20. 02:05
전체 조 과제 피드백 내용
1. 항상 코드의 응집도를 고려하는 것이 중요하다.
좋은 코드란 같은 관심사를 가진 요소들끼리 잘 '응집'되어 있는 상태를 가진다.
또한, 다른 관심사를 가진 요소들끼리는 잘 흩어져 있고, 혼재되어 있지 않은 '낮은 결합도'의 상태를 갖는 것도 좋은 코드의 요건 중 하나이다.
따라서 '높은 응집도', '낮은 결합도'의 원칙을 따르는 것에 집중하자.
2. 가변변수 활용 최소화.
class 같은 모듈 내부에서 필드로 가변변수를 사용해 메소드들이 이 값을 참조해 사용하게 되면 코드 가독성 측면에서 효율이 좋지 않다.
그 이유는 그 가변변수의 값 변화를 계속 추적해야 하며 그 값을 계속 기억하여 코드를 이해해야 하기 때문이다.
또한, 가변변수의 값 변화를 잘못 설계하면 큰 버그가 발생할 여지가 존재한다.
따라서, 가변변수는 어디서나 그 사용을 최소화하고 차라리 함수의 인자로 전달하여 필요한 데이터를 사용하게 하는 것이 가독성, 안정성 측면에서 더욱 효과적이다.
또한, 함수나 메소드 내부에서도 가변변수를 선언하여 분기별로 그 값을 변경해 반환하는 방식보다는, 분기별로 알맞은 결과를 바로 반환하도록 하는 것이 더욱 안정적이다.
// Bad case async fetchSearchedSickList(keyword: string) { this.requestURL = `${BASE_URL}?q=${keyword}`; let searchedSicks = null; try { const cachedSickList = await this.findSearchedSickListInCacheStorage(); if (!cachedSickList) { console.log("calling api"); await this.saveSearchedSickListInCacheStorage(); return await this.findSearchedSickListInCacheStorage(); } searchedSicks = cachedSickList; } catch (e) { const { name } = e as Error; toast.error(name || "정보를 불러오는데 실패했습니다."); } return searchedSicks; } // Good case async fetchSearchedSickList(keyword: string) { try { const cachedSickList = await this.findSearchedSickListInCacheStorage(keyword); if (!cachedSickList) { console.log("calling api"); await this.saveSearchedSickListInCacheStorage(keyword); return await this.findSearchedSickListInCacheStorage(keyword); } searchedSicks = cachedSickList; } catch (e) { const { name } = e as Error; toast.error(name || "정보를 불러오는데 실패했습니다."); return null; } }
즉, 위 코드 예시에서 아래의 메소드 작성 방식이 가변변수인 필드를 제거하고 필요한 변수를 단순히 인자로 넘기고 있으며, 메소드 내부에서 사용되는 가변변수 역시 반환문으로 교체하여 사용하였기 때문에 가독성, 안정성 측면에서 더 뛰어난 작성 방식이라고 할 수 있다.
3. readonly, static 키워드 활용.
class로 인스턴스를 생성할 때, 꼭 메모리에 다시 할당될 필요가 없으며 해당 class로 만들어진 모든 인스턴스가 공유할 수 있는 속성은 static 키워드를 붙여 모듈을 제작하는 것이 메모리 활용 측면에서 효율적이다.
또한, 오직 읽기전용의 속성을 갖도록 의도된 속성들은 readonly 키워드를 붙여 주는 것이 미연의 값 변화를 방지할 수 있으므로 더욱 안정적인 모듈을 설계할 수 있는 방법이다.
// Bad case class SickSearchManager implements ISickSearchManager { private sickCache: Cache | undefined; ... } // Good case class SickSearchManager implements ISickSearchManager { private static sickCache: Cache | undefined; ... }
위 class 모듈에서 sickCache라는 식별자를 가진 브라우저 cache 저장소 객체는 인스턴스 생성 시마다 계속 생성될 필요가 없으므로 static 키워드를 붙여 인스턴스가 아닌 class 자체의 속성으로 설정해 주는 것이 바람직하다.
4. enum 타입 보다 as const를 사용하자.
enum 타입은 typescript에서 사용할 수 있는, 같은 관심사를 가진 상수들을 모아 묶어주는 자료타입이다.
이 타입은 한 번 선언되면 javascript를 통한 변경이 불가능하다.
즉, readonly의 속성을 보인다.
그러나 일반 객체에 as const 키워드를 붙임으로써 enum 타입과 똑같은 기능을 하도록 코드를 작성할 수 있다.
enum CacheError { QUOTA_EXCEEDED = "QuotaExceededError", DOM = "DOMException", }; const cachError = { QUOTA_EXCEEDED = "QuotaExceededError", DOM = "DOMException", } as const;
위 두개의 코드는 readonly, 오직 접근만 가능하고 읽기와 수정은 불가능한 데이터를 만드는 동일한 역할을 수행한다.
그러나 as const를 사용하는 것이 권장되는 점이 있는데, 바로 프로젝트 파일 빌드 시 tree-shaking의 측면에서 이를 찾아볼 수 있다.
외부 라이브러리, 프레임워크를 사용해 프로젝트를 만들고 이를 빌드하면 webpack 등의 빌드 도구는 사용하지 않는 라이브러리, 프레임워크의 js, ts 파일을 배제하고 빌드를 수행하며, 이를 가지치기에 비유하여 tree-shaking이라고 한다.
프로젝트가 온전하게 빌드되는데 필요없는 코드 파일들을 배제하고 꼭 필요한 코드 파일들만 추가하여 빌드가 완료된 프로젝트 파일의 용량을 줄일 수 있도록 하는 개념인 것이다.
즉, enum은 typescript에서만 사용 가능하므로 브라우저가 이를 해석해 온전하게 화면에 출력되게 하려면, 브라우저가 이 enum이란 자료형이 무엇인지 이해하는데 필요한 js 파일의 코드가 빌드된 파일의 코드에 추가된다.
따라서, enum을 사용하는 것보다 as const를 사용해 읽기 전용 데이터를 만드는 것이 프로젝트 용량의 측면에서 효율적이며 이는 다른 라이브러리, 프레임워크의 개념에도 적용될 수 있다.
물론, 팀 구성원의 기호나 컨벤션 측면에서 어떤 코드 작성 방식을 더 선호하느냐가 가장 중점이 되어야하기 때문에, 이를 먼저 인지하고 tree-shaking에 대해 고려하는 것이 가장 바람직할 것이다.
수업 내용
1. 소프트웨어 테스트란?
말 그대로 소프트웨어 안의 기능들이 제대로 동작하는지 확인하는 테스트이다.
기존에는 개발팀 외에 테스트팀이 따로 존재하여 일일히 버그, 오작동 등을 찾아 내었지만 지금은 개발자가 테스트 스크립트를 구현하여 테스팅을 수행하고 있는 추세이다.
즉, 사람이 아닌 컴퓨터로 테스팅을 수행하면 더 빠르게, 휴먼 에러 없이 오작동을 찾아낼 수 있다는 점에서 소프트웨어 테스트는 장점을 가진다.
2. 테스팅의 종류
1) Unit Test
가장 낮은, 가작 적은 범위의 기능을 테스트한다.
개별 함수, 클래스, 메소드 등을 테스트하며 가장 적은 자원이 소모된다.
2) Integration Test
통합 테스트라고 하며, 두 개 이상의 기능을 결합한 결과를 검사하는 테스트이다.
유닛 테스트보다 더 넓은 범위를 테스트하지만 어디까지가 유닛 테스트이고 어디까지가 통합 테스트인지는 주관적이다.
즉, 개인이나 개발 그룹의 문화에 따라 달라질 수 있는 개념이다.
3) End-To-End(E2E) Test
어플리케이션을 실제로 사용하는 것과 비슷한 환경을 구축하고 그 과정 전체를 테스트한다.
위 두 개의 테스트보다 더욱 큰 범위를 다루며 많은 자원이 소모된다.
일반적으로 어플리케이션 전체를 테스트하기 힘들기 때문에, 핵심적인 기능들만 따로 테스트 환경을 구축하여 테스트하는 것이 일반적이다.
3. 많이 사용되는 테스팅 라이브러리.
1) Jest
React를 제작한 Meta에서 만든 javascript 테스팅 도구이며, CRA로 프로젝트를 설치하면 자동으로 설치되어진다.
가장 높은 다운로드 수, 가장 큰 생태계를 자랑하지만 vanilla javascript 외에 SPA 프레임워크에서는 사용이 까다롭다.
2) React-Testing-Library(RTL)
React에서 컴포넌트의 동작과 UI를 테스트할 수 있는 라이브러리이다.
가장 높은 다운로드 수, 가장 큰 생태계를 가졌으며 역시 Meta에서 제작되었다.
이 테스팅 도구에 대하여 주의해야할 점은 이 라이브러리는 로직의 구현이 아니라 단순히 UI와 결과값이 기대값과 일치하는지 여부로만 테스트 성공 유무를 판단하기 때문에 '행위'보다는 '결과'에 초점을 맞춘다는 개념을 이해하여야 한다는 것이다.
4. TDD란?
TDD는 Test-Driven-Development의 약어로서 소프트웨어를 개발하는 여러 방법론 중 하나이다.
기존 코드의 작성방식은 일반적으로 실제 코드 작성을 통한 기능 구현 -> 테스트 코드 구현 -> 테스트가 통과할 때까지 실제 코드 수정 및 테스트를 반복하는 방식으로 이루어졌다.
그러나 TDD를 통한 개발을 수행한다면 이 순서가 역순이 되며 그 정확한 순서는 아래와 같다.
1) Red
실제 구현을 하기 전에, 먼저 실패하는 테스트 코드를 작성하며, 그 후 테스트를 실행한다.
실제 코드가 작성되지 않았기에 테스트 코드는 당연히 실패한다.
2) Green
테스트를 통과하기 위해 가장 간단한 형태로 코드를 작성하며, 그 후 테스트를 실행한다.
테스트는 실제 구현이 되었기에 통과한다.
3) Blue
Green 단계의 코드를 더 좋은 형태로 리팩터링한다.
이 과정에서 지속적으로 테스트를 실행해서 테스트가 통과하는지 확인한다.
이 Red -> Green -> Blue의 순서를 지키면서 개발을 해나가는 것이 TDD이며, 이 방식이 갖는 장점은 아래와 같다.
1)
코드 작성 과정에서 확신 및 자신감을 얻을 수 있게 된다.
코드의 테스트 통과 여부를 실시간으로 알 수 있으므로 즉각적인 피드백 및 오류 검출이 가능하다.
때문에, 초기 개발 속도는 조금 지연될 수 있지만 더욱 안정감있는 개발이 가능하다.
2)
구현을 잘못 한 경우 바로 확인할 수 있다.
테스팅 라이브러리의 즉각적인 피드백에서 오는 장점이다.
디버깅 시간을 효과적으로 단축할 수 있다.
3)
코드의 동작이 명확해진다.
테스트 코드를 작성한다는 것은 현재 자신이 개발하려고 하는 것은 무엇이고 어떠한 로직으로 이루어져야 한다는 것에 대한 이해가 선행되어야하기 때문에, 생각없이 구현에만 달려들게 되는 개발자의 본능을 억제할 수 있다.
따라서, TDD는 좀 더 깊이있는 코드 작성을 자연스럽게 유도한다.
또한, 테스팅 자체가 유닛 테스트처럼 미세한 단위로 이루어지게 된다면 실제 구현 코드 역시 그렇게 따라가기 때문에, 더 깊은 추상화와 모듈화 및 관심사의 분리를 수행할 수 있다는 것도 큰 장점이다.
테스트 공부는 또 언제하지...
PS. 본 내용의 출저는 원티드 프리온보딩 프론트엔드 인턴십의 세션 내용입니다.
'원티드 프리온보딩' 카테고리의 다른 글
1/20 프론트엔드 온보딩 인턴십 내용 정리. (0) 2023.01.23 1/13 프론트엔드 온보딩 인턴십 내용 정리. (0) 2023.01.13 1/10 프론트엔드 온보딩 인턴십 내용 정리. (0) 2023.01.10 1/6 프론트엔드 온보딩 인턴십 내용 정리. (0) 2023.01.07 1/3 프론트엔드 온보딩 인턴십 내용 정리. (0) 2023.01.04