본문 바로가기
공부/Node.js

기초 API 구현

by silverage 2023. 8. 19.

미루고 미루다 이제야 쓰게 되었다....

이번 섹션은 기초 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
      • 실행하기 위해 필요한 모듈들을 저장해둔 폴더
      • 이게 없으면 코드 실행할 때 모듈 못찾았다 하면서 오류뜨고 실행이 되지 않는다

  • 우리가 키워야 할 역량
    • 특정 기술을 주었을 때 알아서 찾아 문제를 해결할 수 있어야 한다.
      • 끊임없이 바뀌는 트렌드
      • 책과 강의도 트렌드에 뒤처져 있을 수 있다.
      • 공식 문서 (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) 정책
  • CORS(Cross-Origin Resource Sharing)
    • 교차 출처 리소스 공유
    • 리소스 호출이 허용된 출처를 서버가 명시해놓으면 출처가 다르더라도 요청과 응답을 주고 받을 수 있도록 만들어놓은 정책
    • 다음 브라우저 → 네이버 백엔드 (다른 출처) 라도 데이터를 공유할 수 있도록 함
      • CORS() 허용 시, 다음에서 네이버로 데이터를 주고받을 수 있음
        • prefilght 요청 → CORS 허용 여부 확인
          ⇒ 응답 후 요청 가능한 것에 대해 API 요청
      • CORS() 거절 시, 교차 출처 리소스 공유 불가
        • preflight 요청 → CORS 거절 응답 → 요청 중지
        • 요청 차단의 경우, 브라우저가 진행함.
        • 백엔드가 차단하는 것이 아닌 브라우저가 차단 여부 결정
        • 거절/승인 여부는 백엔드가 응답 → 요청 차단은 그 응답을 받은 브라우저가 진행
        • 브라우저 없이 하는 요청은 CORS와 상관 없이 요청 가능하다.
    • 브라우저 → API → 다른 출처의 백엔드에 요청 → CORS() 확인[거절 or 허용 범위 등 내용 전달] → 브라우저에 전달
      • 이렇게 가운데에서 대리해주는 것을 프록시 서버라고 함
    • 모바일 앱(브라우저가 아닌, 실제 다운 받아서 사용하는 앱)과도 교환 가능
    • 브라우저만 막는 이유
      • 모바일 앱, 서버로 우회하면 CORS가 필요 없음
        • 애초에 백엔드를 보호하기 위함이 목적이 아니다.
      • 브라우저를 보호하기 위해 CORS가 존재
      • 쿠키
      • CSRF (Cross Site Request Forgery) 공격 등을 막기 위해 브라우저 쪽에서 차단하는 것.

5. 동기/비동기 방식 (Async/Await)

  • 동기 - 기다리자 (기다리고 다음 실행)
    • 요청을 기다릴 필요가 있을 때 진행
    • ex) 파일 저장 후 불러오기 → 저장 도중에 불러오기 하면 파일을 불러올 수 없음
    • async/await → 동기로 하도록 하게 함
  • 비동기 - 기다리지 말자 (안기다리고 다음 실행)
    • 요청들을 서로 기다릴 필요가 없을 때 사용
    • ex) 동시에 여러 일을 할 때 → 보여줄 수 있는 것들 동시에 다 보여주도록 함. 서로가 서로를 기다릴 필요 없음. 서로가 서로와 관련이 없음
    • ex) 게임 다운하면서 카톡하기
    • Promise
  • 외부 API를 요청하는 기능들 (ex. axios) → 기본적으로 비동기로 작동
    • 대부분의 언어들은 동기로 작동하지만 API를 요청하는 기능들은 default 로 비동기로 작동함!
  • 변수
    • var → 변수가 중복 선언되어도 에러가 안 남 → 서비스 장애가 터질 가능성 농후
    • const, let → 변수 중복 선언되면 에러남 → 실수 예방

6. API 만들어보기

6-1. 인증 번호 전송 API

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