미루고 미루다 이제야 쓰게 되었다....
이번 섹션은 기초 API 구현이었는데,
Express 기초, 서버와 포트, Nodemon 적용 및
REST API, GRAPHQL API를 만들었으며,
coolsms를 이용하여 인증 번호 전송 API,
nodemailer를 이용하여 가입 환영 이메일 전송 API를 만드는 것으로 섹션3(22)가 끝이났다.
1. Express 기초 - Express Docs
Express를 이용하여 node.js 프로그램을 만들어보았다.
여기에서는 Express를 다루는 법 또한 중요하지만 주로 npm에 대해 필기를 했다.
- package
- package.json
- dependencies ← 설치했던 파일 (히스토리 목록)
- yarn init 등을 통해 만들어주어야 함 (npm 설치를 위해)
- yarn or npm 명령어의 경우, ls를 했을 때 package.json이 있는 장소에서 명령어를 실행시켜야 한다.
- yarn.lock 파일
- 버전 관리 파일
- 버전이 바뀌면 소스코드가 작동하지 않을 수 있음
→ 다시 패키지를 다운받을 때 같은 버전으로 받아야 함. - 버전은 실무에서 매우 민감한 요소!
- ex) express ⇒ express를 위해 다운 받은 기타 파일들의 버전 또한 관리해주는 역할
- yarn install
- package.json에 무엇이 설치되어있었는지 보고 그걸 다시 설치 후 node_modules라는 폴더를 만들어 실행시키기 위해 필요한 패키지들을 다시 다운 받아줌.
- node_modules
- 실행하기 위해 필요한 모듈들을 저장해둔 폴더
- 이게 없으면 코드 실행할 때 모듈 못찾았다 하면서 오류뜨고 실행이 되지 않는다
- package.json
- 우리가 키워야 할 역량
- 특정 기술을 주었을 때 알아서 찾아 문제를 해결할 수 있어야 한다.
- 끊임없이 바뀌는 트렌드
- 책과 강의도 트렌드에 뒤처져 있을 수 있다.
- 공식 문서 (Docs) 적극적으로 이용
- 한글판은 영문판의 번역이기에 문제가 있을 수 있으니 가급적 영문으로 읽기
- 그에 대해 효율적으로 빠르게 정보를 잘 뽑아 쓸 수 있도록 해야 함
- Ger Started, Installation, Introduction 등 보고 전체적으로 어떤 방식인지 파악, Error-Handling 등 참고해서 에러 발생 시 파악
- 반복해서 보는 책이 Docs여야 한다.
- 특정 기술을 주었을 때 알아서 찾아 문제를 해결할 수 있어야 한다.
2. 서버와 포트
- package.json
- 해당 파일 내에서 실행 명령어를 변경할 수 있음
- node index.js → yarn dev, yarn start:dev 등
- 서버 컴퓨터 (백엔드/DB/프론트엔드 컴퓨터)
- 24시간 켜놓는다.
- 서버실 에어컨 풀가동!
- 백엔드 컴퓨터 - 백엔드 서버 프로그램 (ex. express)
- 포스트맨과 브라우저의 접속을 기다림
- 포트는 0 ~ 65535 중 하나 선택 가능
- 데이터베이스 컴퓨터 - 데이터베이스 서버 프로그램
- 백엔드의 접속을 기다림
- mysql의 경우 기본 포트는 3306
- 프론트엔드 컴퓨터 - 프론트엔드 서버 프로그램
- 브라우저의 접속을 기다림
- 서비스 제공자: 서버 —— 서빙 ——> 이용자: 클라이언트
- 프론트엔드 서버, 백엔드 서버 등을 말함 → 서버 프로그램을 의미
- 앞 뒤 다 빼고 그냥 서버라고 말할 때 → 주로 백엔드 서버를 의미
- 기본 포트의 경우 생략 가능 (http - 80, https - 443)
3. REST API
- REST API
- REST를 기반으로 만들어진 API
- 설계 시 지켜야 하는 규칙
- URI는 동사보다는 명사를, 대문자보다는 소문자를 사용
- 마지막에 슬래시(/)를 포함하지 않음
- 언더바 대신 하이픈 사용
- 파일 확장자는 URI에 포함하지 않음
- 행위를 포함하지 않음 (ex. delete-post, update-post (x) | post (o))
- REST
- Representational State Transfer
- 자원의 이름을 구분하여 해당 자원의 상태를 주고 받는 모든 것
- HTTP URI를 통해 자원 명시 → HTTP Method 이용 → URI에 대한 CRUD 기능을 적용
- 특징
- 서버 클라이언트 구조
- 무상태
- 캐시 처리 기능
- 계층화
- 인터페이스 일관성
- CRUD
- Create(생성), Read(읽기/조회), Update(갱신), Delete(삭제)
- 대부분의 컴퓨터 소프트웨어가 가지는 기본적인 데이터 처리 기능
- Swagger
- REST API를 설계, 빌드, 문서화 및 사용하는 데 도움이 되는 OpenAPI 사양을 중심으로 구축된 오픈 소스 도구 세트
- API만 만들면 안됨! API Docs도 함께 만들어서 프론트에게 이런 API가 있다는 것을 알려줘야 함.
- Node.js 기준 npm: swagger-jsdoc
/** * @swagger * /starbucks: * get: * summary: 커피 메뉴 리스트 가져오기 * description: 커피 메뉴 리스트 가져오기 * tags: [Starbucks] * responses: * 200: * description: 성공 * content: * application/json: * schema: * type: array * items: * properties: * name: * type: string * example: 아메리카노 * kcal: * type: int * example: 5 */
- summary: API 설명
- tags: 그룹
- parameters: 파라미터 넣기
4. CORS
- SOP(Same Origin Policy) 정책
- 같은 출발지의 정책
- 동일한 출처 사이에서만 리소스를 공유할 수 있다는 규칙
- 사용자를 CSRF 등의 공격으로부터 보호하기 위한 1차적인 방어선
- Origin 이란? 프로토콜, 도메인, 포트가 모두 같은 출처를 말함
- 동일 출처: http://example.com/hello | http://example.com/bye
- 다른 출처: http://example.com | https://example.com
- CORS(Cross-Origin Resource Sharing)
- 교차 출처 리소스 공유
- 리소스 호출이 허용된 출처를 서버가 명시해놓으면 출처가 다르더라도 요청과 응답을 주고 받을 수 있도록 만들어놓은 정책
- 다음 브라우저 → 네이버 백엔드 (다른 출처) 라도 데이터를 공유할 수 있도록 함
- CORS() 허용 시, 다음에서 네이버로 데이터를 주고받을 수 있음
- prefilght 요청 → CORS 허용 여부 확인
⇒ 응답 후 요청 가능한 것에 대해 API 요청
- prefilght 요청 → CORS 허용 여부 확인
- CORS() 거절 시, 교차 출처 리소스 공유 불가
- preflight 요청 → CORS 거절 응답 → 요청 중지
- 요청 차단의 경우, 브라우저가 진행함.
- 백엔드가 차단하는 것이 아닌 브라우저가 차단 여부 결정
- 거절/승인 여부는 백엔드가 응답 → 요청 차단은 그 응답을 받은 브라우저가 진행
- 브라우저 없이 하는 요청은 CORS와 상관 없이 요청 가능하다.
- CORS() 허용 시, 다음에서 네이버로 데이터를 주고받을 수 있음
- 브라우저 → API → 다른 출처의 백엔드에 요청 → CORS() 확인[거절 or 허용 범위 등 내용 전달] → 브라우저에 전달
- 이렇게 가운데에서 대리해주는 것을 프록시 서버라고 함
- 모바일 앱(브라우저가 아닌, 실제 다운 받아서 사용하는 앱)과도 교환 가능
- 브라우저만 막는 이유
- 모바일 앱, 서버로 우회하면 CORS가 필요 없음
- 애초에 백엔드를 보호하기 위함이 목적이 아니다.
- 브라우저를 보호하기 위해 CORS가 존재
- 쿠키
- CSRF (Cross Site Request Forgery) 공격 등을 막기 위해 브라우저 쪽에서 차단하는 것.
- 모바일 앱, 서버로 우회하면 CORS가 필요 없음
5. 동기/비동기 방식 (Async/Await)
- 동기 - 기다리자 (기다리고 다음 실행)
- 요청을 기다릴 필요가 있을 때 진행
- ex) 파일 저장 후 불러오기 → 저장 도중에 불러오기 하면 파일을 불러올 수 없음
- async/await → 동기로 하도록 하게 함
- 비동기 - 기다리지 말자 (안기다리고 다음 실행)
- 요청들을 서로 기다릴 필요가 없을 때 사용
- ex) 동시에 여러 일을 할 때 → 보여줄 수 있는 것들 동시에 다 보여주도록 함. 서로가 서로를 기다릴 필요 없음. 서로가 서로와 관련이 없음
- ex) 게임 다운하면서 카톡하기
- Promise
- 외부 API를 요청하는 기능들 (ex. axios) → 기본적으로 비동기로 작동
- 대부분의 언어들은 동기로 작동하지만 API를 요청하는 기능들은 default 로 비동기로 작동함!
- 변수
- var → 변수가 중복 선언되어도 에러가 안 남 → 서비스 장애가 터질 가능성 농후
- const, let → 변수 중복 선언되면 에러남 → 실수 예방
6. API 만들어보기
6-1. 인증 번호 전송 API
- coolsms 사용
6-2. 환영 이메일 전송 API
- nodemailer 사용
- 메일에 따라 CSS의 어떤 버전까지 적용되느냐가 다름!
- 네이버는 최신 적용 가능
- 구글의 경우 최신 CSS는 무시될 수 있음
- 가급적 최신 버전 말고 구버전 html/css 방식을 적용해서 진행해야 어느 메일이던 원하는 대로 할 수 있음
- 최신문법 사용하지 않기!
- 오류 발생 로그 및 해결 (self-signed certificate in certificate chain)
node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^
Error: self-signed certificate in certificate chain
at TLSSocket.onConnectSecure (node:_tls_wrap:1550:34)
at TLSSocket.emit (node:events:514:28)
at TLSSocket._finishInit (node:_tls_wrap:967:8)
at ssl.onhandshakedone (node:_tls_wrap:743:12) {
code: 'ESOCKET',
command: 'CONN'
}
service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_APP_PASSWORD
},
tls: {
rejectUnauthorized: false // 추가하니 오류 해결!
}
7. Mission (추가 과제)
강의에서 배운 내용을 이용해서 추가적으로 과제처럼 내 준다!
기본보다 조금 더 나아갈 수 있도록 해서 나에게 많은 도움이 된 것 같다.
사실 앞전 강의에도 이게 있었는데 올리는걸 까먹었다.........................
지금부터 올려야지
7-1. Quiz 04
- 회원 목록 조회 API 만들기
지금까지 들은 강의에서는 DB를 사용하지 않아 하드코딩을 해서 데이터를 만들어서 조회 API를 만들었다. postman 결과도 잘 나옴!! - app.get('/users', function (req, res) { const result = [ {email: 'aaa@gmail.com', name: '철수', phone: '010-1234-5678', personal: '220110-2222222', prefer: 'https://naver.com'}, {email: 'bbb@gmail.com', name: '철수2', phone: '010-2345-6789', personal: '220110-3333333', prefer: 'https://daum.net'}, {email: 'ccc@gmail.com', name: '철수3', phone: '010-3456-7890', personal: '220110-4444444', prefer: 'https://google.com'}, {email: 'ddd@gmail.com', name: '철수4', phone: '010-1234-7890', personal: '220110-5555555', prefer: 'https://instagram.com'}, {email: 'eee@gmail.com', name: '철수5', phone: '010-2345-7890', personal: '220110-6666666', prefer: 'https://twitter.com'} ]; res.send(result); })
- 커피 목록 조회 API 만들기
위와 동일 - app.get('/starbucks', function (req, res){ const coffee = [ {name: '아이스 카페 아메리카노', kcal: 10}, {name: '아이스 스타벅스 돌체 라떼', kcal: 230}, {name: '아이스 카페 라떼', kcal: 110}, {name: '아이스 카푸치노', kcal: 115}, {name: '씨솔트 카라멜 콜드 브루', kcal: 170}, {name: '돌체 콜드 브루', kcal: 265}, {name: '바닐라 크림 콜드 브루', kcal: 125}, {name: '콜드 브루', kcal: 5}, {name: '카페 아메리카노', kcal: 5}, {name: '카페 라떼', kcal: 110}, {name: '바닐라 스타벅스 더블 샷', kcal: 125} ]; res.send(coffee); })
- API 명세서 만들기
요렇게 만들었다. 저 주석을 달때 약간 많이 아찔해졌었다. - /** * @swagger * /users: * get: * summary: 회원 리스트 가져오기 * description: 회원 목록 조회 * tags: [Users] * responses: * 200: * description: 성공 * content: * application/json: * schema: * type: array * items: * properties: * email: * type: string * example: aaa@gmail.com * name: * type: string * example: 김철수 * phone: * type: string * example: 010-0000-0000 * personal: * type: string * example: 000000-0000000 * prefer: * type: string * example: https://google.com */
7-2. Quiz 05
- REST API를 GraphQL API로 변경하기
여기서는 REST API가 주어지고, 이 REST API를 GraphQL API로 바꾸는게 과제였다. - // index.js const typeDefs = gql` type BoardReturn { number: Int writer: String title: String contents: String } input PhoneInput { phone: String } type Query { fetchBoards: [BoardReturn] } type Mutation { createTokenOfPhone(phoneInput: PhoneInput!): String } `; const resolvers = { Query: { fetchBoards: (_, args) => { return [ { number: 1, writer: "철수", title: "제목입니다", contents: "내용입니다", }, { number: 2, writer: "영희", title: "좋은 날씨입니다", contents: "내용입니다", } ]; }, }, Mutation: { createTokenOfPhone: (_, args) => { // 2-1. 휴대폰번호 자릿수 맞는지 확인하기 const isValid = checkValidationPhone(args.phoneInput.phone); // 2-2. 휴대폰 번호 자릿수가 맞다면 핸드폰 토큰 4자리 만들기 if(isValid){ const token = getToken(); // 2-3. 만든 토큰을 핸드폰번호에 토큰 전송하기 sendTokenToSMS(args.phoneInput.phone, token) return "인증완료"; }else{ return "인증실패"; } }, }, };
- 결과
- 프론트엔드와 API 연동하기 (1/2)
- // menu.js const getCoffee = () => { console.log('index.js 파일의 openMenu 함수 안에서 getCoffee가 실행 됨') // 1. 백엔드 서버로 /starbucks API 요청해 커피 데이터를 받는다. // fetch 사용 fetch('http://localhost:3000/starbucks') // /starbucks 에 api 요청 (get은 parameter 없음) .then(res => res.json()) // api 불러오면 res 값을 json으로 변경 .then( // 그 중에서 json 값을 뽑아넴 data => { // 2. 받은 데이터로 createMenuCard 함수를 이용해 메뉴 카드를 모두 만들어주세요. data.forEach(element => { // json 으로 된 데이터 배열 받아오기에 반복문 돌림 createMenuCard(element) }); } ); }
7-3. Quiz 06
- 프론트엔드와 API 연동하기 (2/2)
- 휴대폰 인증 토큰 발급 API
- const getValidationNumber = async () => { const phoneNum = document.querySelector('#PhoneNumber01').value + document.querySelector('#PhoneNumber02').value + document.querySelector('#PhoneNumber03').value; fetch('http://localhost:3000/tokens/phone', { method: "POST", headers: { "Content-Type": 'application/json', }, body: JSON.stringify({ phone: phoneNum }) }).then(res => console.log(res)); document.querySelector('#ValidationInputWrapper').style.display = 'flex' console.log('인증 번호 전송') }
- 실행 결과: 잘 왔다
- 회원 가입 및 환영 메일 전송 API
- // 회원 가입 API 요청 const submitSignup = async () => { const phoneNum = document.querySelector('#PhoneNumber01').value + document.querySelector('#PhoneNumber02').value + document.querySelector('#PhoneNumber03').value; const personalNum = document.querySelector('#SignupPersonal').value + "-" + document.querySelector('#SignupPersonal02').value ; fetch('http://localhost:3000/users', { method: "POST", headers: { "Content-Type": 'application/json', }, body: JSON.stringify({ name: document.querySelector('#SignupName').value, personal: personalNum, phone: phoneNum, prefer: document.querySelector('#SignupPrefer').value, email: document.querySelector('#SignupEmail').value, password: document.querySelector('#SignupPwd').value }) }).then(res => console.log(res)); console.log('회원 가입 이메일 전송') }
- 실행 결과 -> 잘 왔다2
- 환경변수 분리하기
- 회원 가입 이메일 전송 및 문자로 인증 번호 보낼 때 실제 번호와 Key 값을 입력해야 했는데, 이 부분은 보안적으로 노출되지 않도록 하는 것이 좋다. 따라서 .env 파일을 만들어 Key 값 등 보안적으로 노출되지 않아야 하는 값들을 넣어 환경 변수로 분리해 주었다.
8. 더욱 공부해봐야 할 부분
- rejectUnauthorized <- 이 내용이 없으면 위와 같은 오류가 뜨는데 그걸 조금 더 알아봐야 할 것 같다.
- 그리고 영어공부하기.... ㅎ
'공부 > Node.js' 카테고리의 다른 글
Scraping (0) | 2023.08.19 |
---|---|
Docker (0) | 2023.08.19 |
데이터 통신 (0) | 2023.08.19 |
Node.js 활용-1 (0) | 2023.08.19 |
Node.js 스터디를 시작하며 (1) | 2023.08.19 |