Node Node.js 테스트 및 품질 모범 사례에 대한 포괄적 인 10 시간 코스
https://github.com/goldbergyoni/nodebestpractices#readme
https://twitter.com/nodepractices/
https://github.com/goldbergyoni/nodebestpractices/blob/master/README.korean.md
안녕하세요! 먼저 알아야 할 3가지가 있습니다:
1. 이 문서를 읽는 것은, 사실상 수십 개의 베스트 Node.js 문서를 읽는 것입니다. - 이 문서는 Node.js 의 가장 인기 있는 모범사례(Best Practice)들을 모은 요약집 및 큐레이션입니다.
2. 가장 큰 모음집이며, 매주 성장하고 있습니다. - 현재, 50개 이상의 모범사례들과, 스타일 가이드, 아키텍처적인 팁들이 제공되고 있습니다. 이 문서의 업데이트를 위해 새로운 이슈들과 PR들이 매일 만들어지고 있습니다. 우리는 이 문서의 잘못된 코드를 고치거나 새로운 아이디어들을 제안하는 것을 매우 환영합니다. 마일스톤 보러가기
3. 항목 대부분은 추가적인 정보가 있습니다 - 항목 옆쪽에 존재하는 ?자세히 보기 링크에서 코드 예제, 참조 블로그 또는 기타 정보들을 확인 할 수 있습니다.
목차
1. 프로젝트 구조 설계
1.1 컴포넌트 기반으로 설계하라
핵심요약: 큰 프로젝트에서 빠지기 쉬운 최악의 함정은 많은 수백개의 의존성을 가진 커다란 소스코드를 유지 보수하는 것이다. 그렇게 하나로 통째로 짜여진 코드는 개발자가 새로운 기능들을 협업하는 속도를 느려지게 한다. 그 대신에 당신의 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 가지게 하고 컴포넌트의 각 단위가 작고 간단하게 유지되도록 하라. 아래의 '자세히 보기'를 눌러 올바른 프로젝트 구조의 예시를 확인하라.
그렇게 하지 않을 경우: 새로운 기능을 작성하는 개발자가 변경사항이 미치는 영향을 깨닫기위해 몸부림치거나 의존하고 있는 다른 컴포넌트를 망칠까봐 두려워 할때 배포는 느려지고 더 위험해진다. 비지니스 단위가 나눠져 있지 않으면 확장(scale-out)하기도 쉽지 않다.
구성 요소별로 솔루션 구성
한 단락 설명자
중간 크기 이상의 앱의 경우 모놀리스는 실제로 나쁩니다.
많은 종속성을 가진 하나의 큰 소프트웨어를 갖는 것은 추론하기가 어렵고 종종 스파게티 코드로 이어집니다.
야수를 길들여 '모듈화'할 수 있는 기술을 갖춘 숙련된 건축가조차도 디자인에 많은 정신적 노력을 기울이고 있으며, 각 변경 사항은 다른 종속물에 미치는 영향을 신중하게 평가해야 합니다.
궁극적 인 해결책은 작은 소프트웨어를 개발하는 것입니다.
전체 스택을 다른 사람과 파일을 공유하지 않는 독립적 인 구성 요소로 나누고, 각 파일은 매우 적은 수의 파일 (예 : API, 서비스, 데이터 액세스, 테스트 등)을 구성합니다.
그것에 대해 추론하기 쉽습니다.
일부는 이 '마이크로 서비스'아키텍처라고 부를 수 있습니다.
마이크로 서비스는 반드시 따라야 하는 사양이 아니라 일련의 원칙이라는 것을 이해하는 것이 중요합니다.
많은 원리를 본격적인 마이크로 서비스 아키텍처로 채택하거나 소수의 원칙 만 채택 할 수 있습니다.
소프트웨어 복잡성을 낮게 유지하는 한 둘 다 좋습니다.
최소한 컴포넌트간에 기본 경계를 작성하고, 각 비즈니스 컴포넌트에 대해 프로젝트 루트에 폴더를 지정하고 자체 포함 시키십시오.
다른 컴포넌트는 공용 인터페이스 또는 API를 통해서만 기능을 사용할 수 있습니다.
이것은 구성 요소를 단순하게 유지하고 의존성을 피하고 앱이 커지면 미래에 완전한 마이크로 서비스를 구현할 수 있는 토대입니다.
블로그 인용문 : "확장하려면 전체 응용 프로그램을 확장해야 합니다"
블로그 MartinFowler.com에서
모놀리식 애플리케이션은 성공할 수 있지만 점점 더 많은 애플리케이션이 클라우드에 배치 될수록 점점 더 많은 사람들이 좌절감을 느끼고 있습니다. 변경 주기는 서로 연결되어 있습니다. 응용 프로그램의 일부를 변경하면 전체 모노리스를 다시 빌드하고 배포해야 합니다.
시간이 지남에 따라 좋은 모듈 식 구조를 유지하기가 어려운 경우가 많으므로 해당 모듈 내 하나의 모듈에만 영향을 미치는 변경 사항을 유지하기가 더 어려워집니다.
확장에는 더 많은 리소스가 필요한 일부가 아닌 전체 응용 프로그램의 확장이 필요합니다.
블로그 인용문 : "응용 프로그램의 아키텍처가 무엇을 외치나요?"
블로그 uncle-bob에서
...if you were looking at the architecture of a library, you’d likely see a grand entrance, an area for check-in-out clerks, reading areas, small conference rooms, and gallery after gallery capable of holding bookshelves for all the books in the library. That architecture would scream: Library.
그렇다면 애플리케이션 아키텍처가 무엇을 외치나요? 최상위 디렉토리 구조와 최상위 레벨 패키지의 소스 파일을 볼 때; 건강 관리 시스템, 회계 시스템 또는 재고 관리 시스템이 소리를 지르나요? 아니면 비명을 지르나요? Rails, Spring / Hibernate 또는 ASP?
장점 : 자체 포함 된 구성 요소로 솔루션 구성
나쁨 : 기술 역할별로 파일을 그룹화
1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 둬라.
핵심요약: 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려를 깨끗하게 분리할 뿐만 아니라 모의 객체를 만들거나(mocking) 테스트하기가 굉장히 쉽게 만든다. 이것이 굉장히 일반적인 패턴임에도, API 개발자는 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 그렇게 하는 것은 당신의 어플리케이션에 의존성을 만들고 Express에서만 접근 가능하도록 만든다.
그렇게 하지 않을 경우: 웹 객체를 다른 계층과 뒤섞은 앱은 테스트 코드, CRON 작업이나 Express가 아닌 다른 곳에서 접근이 불가능하게 한다.
앱을 계층화하고 경계 내에서 Express 유지
웹, 서비스 및 DAL : 구성 요소 코드를 계층으로 분리
1 분 설명자 : 혼합 층의 단점
1.3 유틸리티들을 NPM 패키지로 감싸라(wrap)
핵심요약: 커다란 코드 기반으로 구성되어있는 커다란 앱에서는 로깅, 암호화 같은 횡단 관심사(cross-cutting-concern)가 존재하는 유틸의 경우 당신 자신의 코드로 감싸져야 하며 개인 NPM package로 노출이 되어야 한다. 이것은 여러 코드 기반과 프로젝트들 사이에서 그것들을 공유가 가능하도록 해준다.
그렇게 하지 않을 경우: 당신 자신만의 배포 및 의존성 바퀴(wheel)를 새로 발명해야 할 것이다.
일반적인 유틸리티를 npm 패키지로 포장
한 단락 설명자
성장을 시작하고 비슷한 유틸리티를 사용하는 다른 서버에 다른 구성 요소가 있으면 종속성 관리를 시작해야 합니다. 유틸리티 코드 사본 1 개를 유지하고 여러 소비자 구성 요소를 사용하고 배포 할 수 있는 방법은 무엇입니까? 글쎄, 그것을 위한 도구가 있다.
그것은 npm이라고 불린다. 나중에 쉽게 교체 할 수 있도록 써드 파티 유틸리티 패키지를 자신의 코드로 감싸서 자신의 코드를 개인 npm 패키지로 공개한다.
이제 모든 코드 기반에서 해당 코드를 가져 와서 무료 종속성 관리 도구를 활용할 수 있습니다.
개인 모듈, 개인 레지스트리 또는 로컬 npm 패키지를 사용하여 공개적으로 공유하지 않고도 개인 용도로 npm 패키지를 게시 할 수 있습니다.
환경과 구성 요소간에 고유 한 유틸리티 공유
1.4 Express의 app과 server를 분리하라
핵심요약: 'Express' 정의를 적어도 API 선언(app.js)과 네트워크 부분(WWW)의 두 개 파일로 나눠서 전체 Express앱을 하나의 큰 파일에 정의하는 불쾌한 습관을 피해라. 더 좋은 구조는 API 선언을 컴포넌트에 위치 시키는 것이다.
그렇게 하지 않을 경우: API는 HTTP 요청으로만 테스트가 가능할 것이다(커버리지 보고서를 생성하기가 더 느려지고 훨씬 힘들어진다). 수백 줄의 코드를 하나의 파일에서 관리하는 것이 크게 즐겁지는 않을 것이다.
별도의 Express '앱'과 '서버'
한 단락 설명자
최신 Express 생성기는 유지할 가치가 있는 모범 사례를 제공합니다. API 선언은 네트워크 관련 구성 (포트, 프로토콜 등)과 분리되어 있습니다. 이를 통해 네트워크 호출을 수행하지 않고도 프로세스 내에서 API를 테스트 할 수 있습니다. 빠른 테스트 실행 및 코드의 커버리지 메트릭 가져 오기와 같은 모든 이점을 제공합니다. 또한 유연하고 다른 네트워크 조건에서 동일한 API를 배포 할 수 있습니다. 보너스 : 우려 사항 분리 및 코드 정리
코드 예 : API 선언, app.js에 있어야 함
var app = express(); app.use(bodyParser.json()); app.use("/api/events", events.API); app.use("/api/forms", forms);
코드 예 : 서버 네트워크 선언은 /bin/www에 있어야 합니다.
var app = require('../app'); var http = require('http'); /** * Get port from environment and store in Express. */ var port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * Create HTTP server. */ var server = http.createServer(app);
예 : supertest (인기 테스트 패키지)를 사용하여 프로세스 내 API 테스트
const app = express(); app.get('/user', function(req, res) { res.status(200).json({ name: 'tobi' }); }); request(app) .get('/user') .expect('Content-Type', /json/) .expect('Content-Length', '15') .expect(200) .end(function(err, res) { if (err) throw err; });
1.5 환경을 인식하는, 보안적인, 계층적인 설정을 사용하라
핵심요약: 완벽하고 결점이 없는 구성 설정은 (a) 파일과 환경 변수에서 키 값을 읽을 수 있어야 하고 (b) 보안 값들은 커밋된 코드 바깥에서 관리 되어야 하고 (c) 설정은 좀 더 쉽게 찾을 수 있도록 계층적으로 관리해야 한다. rc, nconf, config와 같이 이러한 요구사항을 동작하게 해주는 몇가지 패키지가 존재한다.
그렇게 하지 않을 경우: 위의 구성 요구사항 중 어느 것도 만족 시키지 못한다면 개발팀 혹은 데브옵스팀을 늪으로 몰아갈 수 있다. 아마도 두 팀 모두일 것이다.
환경 인식, 보안 및 계층 적 구성 사용
한 단락 설명자
구성 데이터를 다룰 때 많은 것들이 성 가시고 느려질 수 있습니다.
일부 구성 라이브러리는 이러한 기능을 대부분 무료로 제공 할 수 있으며 이러한 요구 사항을 대부분 충족시키는 rc, nconf 및 config와 같은 npm 라이브러리를 살펴보십시오.
코드 예 – 계층 적 구성은 항목을 찾고 대규모 구성 파일을 유지하는 데 도움이 됩니다.
{ // Customer module configs "Customer": { "dbConfig": { "host": "localhost", "port": 5984, "dbName": "customers" }, "credit": { "initialLimit": 100, // Set low for development "initialDays": 1 } } }
2. 에러 처리 방법
2.1 비동기 에러 처리시에는 async-await 혹은 promise를 사용하라
핵심요약: 비동기 에러를 콜백 스타일로 처리하는 것은 지옥으로 가는 급행열차일 것이다(운명의 피라미드로 잘 알려진). 당신이 코드에 줄 수 있는 가장 큰 선물은 평판이 좋은 promise 라이브러리를 사용하거나 훨신 작고 친숙한 코드 문법인 try-catch를 사용하게 해주는 async-await를 사용하는 것이다.
그렇게 하지 않을 경우: Node.js 콜백 스타일인 function(err, response)는 에러 처리와 일반 코드의 혼합, 코드의 과도한 중첩, 이상한 코딩 패턴 때문에 유지 보수가 불가능한 코드로 가는 확실한 길이다.
비동기 오류 처리를 위해 비동기 대기 또는 약속 사용
한 단락 설명자
대부분의 프로그래머는 익숙하지 않기 때문에 콜백이 제대로 확장되지 않습니다. 그들은 오류를 온전히 확인하고 불쾌한 코드 중첩을 처리하며 코드 흐름을 추론하기 어렵게 만듭니다. BlueBird, async 및 Q와 같은 약속 라이브러리는 RETURN 및 THROW를 사용하여 프로그램 흐름을 제어하는 표준 코드 스타일을 압축합니다. 특히, 주요 코드 경로가 모든 함수의 오류를 처리하지 않도록 하는 선호하는 try-catch 오류 처리 스타일을 지원합니다.
코드 예 – 약속을 사용하여 오류 포착
doWork() .then(doWork) .then(doOtherWork) .then((result) => doWork) .catch((error) => {throw error;}) .then(verify);
안티 패턴 코드 예제 – 콜백 스타일 오류 처리
getData(someParameter, function(err, result) { if(err !== null) { // do something like calling the given callback function and pass the error getMoreData(a, function(err, result) { if(err !== null) { // do something like calling the given callback function and pass the error getMoreData(b, function(c) { getMoreData(d, function(e) { if(err !== null ) { // you get the idea? } }) }); } }); } });
블로그 인용문 : "약속에 문제가 있습니다"
블로그 pouchdb.com에서
…… 실제로 콜백은 훨씬 더 사악한 일을 합니다. 즉, 우리에게 프로그래밍 언어에서 당연한 것으로 여겨지는 스택을 빼앗아갑니다. 스택 없이 코드를 작성하는 것은 브레이크 페달 없이 자동차를 운전하는 것과 매우 유사합니다. 도달 할 때까지 자동차가 얼마나 나쁘게 필요한지 알지 못합니다. 약속의 요점은 우리가 비 동기화 할 때 잃어버린 언어 기초, 즉 리턴, 던지기 및 스택을 우리에게 돌려주는 것입니다. 그러나 약속을 활용하려면 약속을 올바르게 사용하는 방법을 알아야 합니다.
블로그 인용문 : "약속은 훨씬 더 간결하다"
블로그 gosquared.com에서
……… 약속 방법은 훨씬 더 간결하고 명확하며 더 빨리 쓸 수 있습니다. ops 내에서 오류나 예외가 발생하면 단일 .catch () 핸들러에서 처리합니다. 이 단일 위치에서 모든 오류를 처리하면 작업의 각 단계마다 오류 검사를 작성할 필요가 없습니다.
블로그 인용문 : "약속은 기본 ES6이며 생성기와 함께 사용할 수 있습니다"
StrongLoop 블로그에서
…. 콜백에는 오류 처리 이야기가 거칠어집니다. 약속이 더 좋습니다. 약속과 함께 Express의 기본 제공 오류 처리와 결혼하고 포착되지 않은 예외의 가능성을 크게 줄입니다. 약속은 기본 ES6이며 생성기와 함께 사용할 수 있으며 Babel과 같은 컴파일러를 통해 async / await와 같은 ES7 제안
블로그 인용문 : "당신이 사용했던 모든 일반적인 흐름 제어 구조는 완전히 손상되었습니다"
Benno의 블로그에서
…… 비동기 콜백 기반 프로그래밍의 가장 좋은 점 중 하나는 기본적으로 사용자가 사용하던 모든 일반적인 흐름 제어 구성이 완전히 손상되었다는 것입니다. 그러나 가장 깨진 것으로 보이는 것은 예외 처리입니다. Javascript는 예외를 처리하기 위해 매우 친숙한 try… catch 구문을 제공합니다. 예외의 문제점은 콜 스택에서 오류를 바로 잡는 훌륭한 방법을 제공하지만 다른 스택에서 오류가 발생하면 완전히 쓸모가 없다는 것입니다.
2.2 내장된 Error 객체만 사용하라
핵심요약: 많은 사람들이 문자열이나 사용자가 임의로 정의한 타입으로 에러를 던진다(throw). 이것은 에러 처리 로직과 모듈 사이의 상호 운영성을 복잡하게 한다. 당신이 promise를 거부(reject)하든, 예외를 던지든, 에러를 냈건 내장된 Error 객체를 이용하는 것은 균일성을 향상하고 정보의 손실을 방지하게 만들 것이다.
그렇게 하지 않을 경우: 일부 컴포넌트를 호출할때 어떤 에러의 타입이 반환될 지 불확실해져서 적절한 에러 처리가 매우 어려워질 것이다. 더 나쁜 것은, 사용자가 정의한 타입으로 에러를 나타내는 것은 스택 정보(stack trace)와 같은 중요한 에러 정보를 손실 할 가능성이 있다는 것이다!
내장 Error 객체 만 사용
한 단락 설명자
다양한 코드 흐름 옵션 (예 : EventEmitter, Callbacks, Promises 등)과 함께 JS의 허용되는 특성은 개발자가 오류를 발생 시키는 방식에 큰 차이를 가져옵니다. 일부는 문자열을 사용하고 다른 것은 고유 한 사용자 정의 유형을 정의합니다. Node.js 내장 Error 객체를 사용하면 코드 내에서 및 타사 라이브러리와의 균일 성을 유지하는 데 도움이 되며 StackTrace와 같은 중요한 정보도 보존됩니다. 예외를 제기 할 때는 일반적으로 오류 이름 및 관련 HTTP 오류 코드와 같은 추가 컨텍스트 속성으로 채우는 것이 좋습니다. 이 균일 성과 실습을 달성하려면 추가 속성을 사용하여 Error 객체를 확장하십시오. 아래 코드 예제를 참조하십시오.
코드 예 – 올바르게 수행
// throwing an Error from typical function, whether sync or async if(!productToAdd) throw new Error("How can I add new product when no value provided?"); // 'throwing' an Error from EventEmitter const myEmitter = new MyEmitter(); myEmitter.emit('error', new Error('whoops!')); // 'throwing' an Error from a Promise const addProduct = async (productToAdd) => { try { const existingProduct = await DAL.getProduct(productToAdd.id); if (existingProduct !== null) { throw new Error("Product already exists!"); } } catch (err) { // ... } }
코드 예 – 안티 패턴
// throwing a string lacks any stack trace information and other important data properties if(!productToAdd) throw ("How can I add new product when no value provided?");
코드 예제 – 더 잘 수행
// centralized error object that derives from Node’s Error function AppError(name, httpCode, description, isOperational) { Error.call(this); Error.captureStackTrace(this); this.name = name; //...other properties assigned here }; AppError.prototype = Object.create(Error.prototype); AppError.prototype.constructor = AppError; module.exports.AppError = AppError; // client throwing an exception if(user == null) throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true)
등록된 댓글이 없습니다.