ES(ECMA Script)

ES란, ECMA International이 ECMA-262 기술 수격에 따라 정의하고 있는 표준화된 스크립트 프로그래밍 언어이다.

JavaScript의 표준화를 위한 규격을 의미

 

ES6

ES는 지속적으로 업데이트되고 있는데, ES 뒤에 붙은 숫자가 버전이라고 생각하면 된다.

ES6은 6번째 버전이라고 생각하면 되는데

ES5 이하 버전에서 문제가 되었던 부분들이 해결되고, 많은 기능들이 추가되어서 가독성과 유지 보수 측면에서 큰 향상을 가져왔기 때문에

더 중요하게 다뤄지고 있다.

 

Node.js의 경우, ES6 이상의 최신 자바스크립트 문법으로 작성된 코드가 Node.js에서 실행되지 않는 경우가 종종 있다.

여러 방법이 존재하지만, 바벨(Babel)이라는 트랜스파일러를 이용하였다.

 

Babel

ES6 이상의 버전 등의 코드를 하위 버전의 자바스크립트 코드로 변호나시켜 제대로 동작할 수 있도록 하는 역할

 

저번 실습에서 바벨을 깔았는데도 깔기 전과 같은 오류(ES 모듈 관련)가 떴다.

'.babelrc' 또는 'babel.config.json' 파일을 수정하여

Babel이 ES 모듈을 올바르게 처리하도록 설정해야 했다.

'.babelrc' 파일을 생성 또는 수정하여

ES 모듈을 CommonJS 모듈로 변환하는 설정을 추가하면 된다.

나는 파일이 없길래 프로젝트 루트 디렉토리에 새로 생성하였다.

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    "@babel/plugin-transform-modules-commonjs"
  ]
}

위 코드를 추가하여 다시 yarn start를 실행하였더니 오류가 해결되었다.

 

프로젝트 구조

다양한 구조들이 고안되어 있다.

프로젝트마다 사용하기에 편하고, 적합하다고 판단되는 구조로 선택하면 된다.

 

3 Layer Architecture

비즈니스 로직을 분리하는 것을 목적으로 한다.다른 계층에 영향을 주지 않고 특정 계층만 수정 및 확장할 수 있기 때문에유지 보수 및 확장 시 조금 더 편하게 작업할 수 있다는 것이 장점이다.

  • Controller Layer
    • 클라이언트의 요청을 받아 서비스에 전달
    • 서비스에서 작업을 마친 데이터를 받아 클라이언트에게 응답
    • 단지 요청을 받아 응답 처리를 진행한다. (routing)
  • Service Layer
    • 비즈니스 로직 캡슐화 및 추상화
    • Controller로 부터 전달된 요청에 로직을 적용하는 계층
  • Data Access Layer (Repository Layer)
    • 쿼리를 수행하며 DB와 상호작용
    • Service 계층에서 데이터베이스 접근이 필요한 경우가 발생하는데 데이터에 대한 코드가 작성되는 계층

클라이언트에서 요청 -> Controller 요청 받고 Service에 전달 -> Service는 DB가 필요한 경우, DAO를 통해 DB와 상호작용하여 얻어낸 결과로 비즈니스 로직 처리 -> 처리 결과를 Controller에게 전달하고 응답을 클라이언트에게 전달

 

라우팅 관련 코드는 controller, 서비스 관련 코드는 service/provider, 데이터 관련 코드는 repository에서 관리한다.

 

DTO (Data Transfer Object)

데이터를 객체로 옮기는 역할

계층 간 데이터를 전송할 때도 이용되고 클라이언트로부터 전송 받은 데이터를 객체로 변환할 때도 사용된다.

 

DTO를 사용해야 하는 이유 ?

데이터를 받아오는 과정에서 하나가 빠질 수도 있고, 원하는 형태로 전달되지 않을 수도 있다.

그럴 때 유효성 검사 등을 진행하며 오류가 발생하지 않도록 할 수 있다.

전달되는 데이터가 달라질 경우, DTO의 내용만 수정하면 Service를 건들지 않아도 되니 이 또한 편리하다.

Service에서 req.body의 jsonObject를 바로 사용하는 것이 아니라,

Controller 단에서 req.body를 DTO 형태로 받아 Service에게 DTO로 값을 전달한다.

라고 생각하면 된다.

 

DTO를 어디서 만들어야 할까 ?

1. Service에서 생성

- Service -> Controller

- DTO로 데이터를 걸러 컨트롤러를 작업하기에 DB의 민감한 정보를 숨길 수 있다.

 

2. Controller에서 생성

- Controller -> Service

- 서비스 함수의 범용성이 늘어나서 유지 보수에 용이하다.

 

동일한 DB 테이블을 필요로 하는 여러 컨트롤러 등의 함수를 재활용할 수 있어 코딩이 더욱 편해진다.

 

디렉토리 구조 (폴더 구조)

디렉토리 구조에 정답은 없다. 편한 대로 만드는 것이 정답

 

1. 계층 별로 (기능 별)

 

2. 모델 별로 (도메인형)

 

키워드 정리

  • ES
    • ECMAScript(ES)는 JavaScript의 표준 사양으로, 자바스크립트의 공식 이름이다.
    • ES는 ECMA 인터네셔널이 제정한 스크립트 언어 사양으로, 웹 개발을 포함한 다양한 애플리케이션에서 사용된다.
    • ES 사양은 주기적으로 업데이트된다.
  • ES6
    • ES6의 주요 변화 및 특징
      • 추가된 기능
        • let, const : 블록 범위 변수를 선언할 수 있는 키워드
        • Arrow Functions : 간결한 함수 표현식
        • Template Literals : 문자열 내에서 변수를 포함할 수 있는 백틱(’’’) 문자열
        • Destructuring Assignment : 배열 및 객체의 값을 쉽게 추출할 수 있는 문법
        • Classes : 객체 지향 프로그래밍을 지원하는 클래스 문법
        • Promises : 비동기 작업을 관리하기 위한 새로운 객체
    • ES6를 중요시 하는 이유
      • 현대적인 자바스크립트 개발의 기본
      • 많은 개선과 새로운 기능 도입
      • 최신 기능을 활용하여 더 효율적이고 성능이 우수한 코드를 작성할 수 있음
  • Babel(바벨)
    • 바벨 이란?예를 들어, 화살표 함수나 클래스와 같은 ES6 문법을 기존의 함수 표현식과 프로토타입 기반 코드로 변환한다.플러그인은 특정 JavaScript 기능을 변환하는 역할을 하며, 프리셋은 여러 플러그인을 모아놓은 구성바벨 설정은 프로젝트의 루트 디렉토리에 있는 ‘babel.config.json’ 또는 ‘.babelrc’ 파일에서 구성할 수 있고, 이 파일에서 사용할 프리셋과 플러그인을 지정한다.
    • 바벨은 기본적으로 코드 변환을 수행하지만, 새로운 API나 내장 객체가 구현 환경에서 사용될 수 있도록 하는 polyfill도 제공한다.
    • 바벨은 플러그인과 프리셋을 사용하여 변환 과정을 유연하게 구성할 수 있다.
    • 바벨(Babel)은 최신 자바스크립트 기능을 지원하지 않는 구형 브라우저에서도 코드를 실행할 수 있도록 ES6/ES2-15 이상의 코드를 ES5 이하의 코드로 변환한다.
    • 바벨을 사용하는 이유
      • 바벨은 최신 JavaScript 기능을 사용하면서도 다양한 브라우저 환경을 지원할 수 있도록 도와준다.
      • 개발자는 최신 표준의 장점을 활용하여 더 나은 성능과 가독성을 가진 코드를 작성할 수 있으며, 동시에 구형 브라우저 호환성 문제를 해결할 수 있다.
      • 바벨은 특히 프론트엔드 개발에서 중요한 도구로 자리 잡고 있으며, 현대적인 프레임워크 및 라이브러리와 함께 널리 사용됨.
  • 프로젝트 아키텍처
    • 프로젝트 아키텍처가 중요한 이유
      1. 일관된 설계 및 개발
        1. 통일된 구조 : 프로젝트의 기본 구조를 정의하여 모든 팀원이 동일한 방식으로 시스템을 이해하고 접근할 수 있도록 함
        2. 재사용성 : 코드와 모듈의 재사용성 높임
      2. 확장성
        1. 모듈화 : 시스템을 독립적인 모듈로 분할하여 각각의 모듈이 독립적으로 개발되고 테스트될 수 있게 함 → 새로운 기능 추가나 확장이 용이함
        2. 유연성 : 시스템이 변경 요구사항에 유연하게 대응할 수 있도록 해줌
      3. 유지보수성
        1. 가독성 : 코드의 가독성을 높이고, 개발자들이 쉽게 이해하고 수정할 수 있게 해줌
        2. 문제 해결 : 아키텍처가 잘 정의되어 있으면 문제가 발생했을 때 원인을 빠르게 파악하고 해결할 수 있음
      4. 성능
        1. 최적화 : 시스템 성능을 최적화할 수 있도록 설계됨 → 리소스 사용 최소화, 시스템 응답 속도 향상
        2. 로드 밸런싱 : 대규모 트래픽을 처리할 수 있도록 로드 밸런싱을 구현하여 안정성 높임
      5. 프로젝트 관리
        1. 예측 가능성 : 프로젝트의 진행상황을 예측하고, 일정과 비용을 더 정확히 계획할 수 있음
        2. 위험 감소 : 초기 단계에서 아키텍처를 정의함으로써 잠재적인 기술 문제와 리스크를 사전에 식별하고 관리
    • 3-Layer-Architecture
      • 소프트웨어 애플리케이션을 세 개의 주요 계층으로 분리하여 개발하고 관리하는 설계 방식
      • Controller Layer : 사용자 인터페이스와 관련된 모든 것 담당, 사용자 입력을 수집하고 이를 Service Layer에 전달, 서비스 계층으로부터 받은 데이터를 사용자에게 표시
      • Service Layer : 애플리케이션의 핵심 기능과 로직 처리, 컨트롤러 계층과 데이터 접근 계층 간의 중재
      • Data Access Layer : 데이터베이스와의 상호작용 처리, 데이터베이스 연결 / SQL 쿼리 실행 / 데이터베이스 트랜잭션 관리 등을 담당
      • 각 계층이 명확하게 분리되어 있어, 독립적으로 개발되고 유지보수될 수 있다.
    • MVC 패턴
      • 소프트웨어 디자인 패턴 중 하나로, 애플리케이션을 세 가지 주요 구성 요소인 모델(Model), 뷰(View), 컨트롤러(Controller)로 분리하여 구조화한다..
      • 애플리케이션의 데이터, 사용자 인터페이스, 이 둘간의 상호작용을 분리하여 유지보수와 확장성을 높인다.
      • 모델 : 애플리케이션의 데이터 및 비즈니스 로직 담당, 데이터베이스와의 상호작용
      • 뷰 : 사용자 인터페이스 담당, 데이터가 변경될 때마다 이를 반영하여 화면 업데이트
      • 컨트롤러 : 모델과 뷰를 중재하는 역할
      • 유지보수성, 재사용성, 유연성, 테스트 용이성 등과 같은 장점 존재
    • 그 외의 프로젝트 구조
      1. 모놀리식 아키텍처 ( Monolithic Architecture )
        • 모든 기능이 하나의 코드베이스 안에 포함된 구조
        • 모든 컴포넌트가 단일 배포 단위로 함께 배포된다.
        • 단순한 배포와 성능 최적화가 쉽다는 장점 존재
        • 규모가 커질 수록 유지보수가 어렵고, 특정 기능에 대한 변경이 전체 애플리케이션에 영향을 미친다는 단점 존재
      2. 마이크로서비스 아키텍처 ( Microservices Arichitecture )
        • 애플리케이션을 여러 개의 독립적인 서비스로 분리하여 개발하고 배포하는 구조
        • 각 서비스는 독립적으로 배포되고, 서로 다른 기술 스택을 사용할 수 있음
        • 특정 서비스에 대한 변경이 다른 서비스에 영향을 미치지 않고, 다양한 기술 스택을 쓸 수 있다는 장점 존재
        • 배포와 관리가 복잡하고, 서비스 간 통신의 오버헤드 및 데이터 일관성 유지가 어렵다는 단점 존재
      3. 분산 아키텍처 ( Distributed Architecture )
        • 애플리케이션의 컴포넌트가 네트워크를 통해 분산되어 동작하는 구조
        • 클라우드 기반 애플리케이션이나 대규모 시스템에서 자주 사용
        • 높은 확장성과 고가용성의 장점 존재
        • 통신 관리가 복잡하고 데이터 일관성 문제가 존재한다는 단점 존재
  • 비즈니스 로직
    • 소프트웨어 애플리케이션에서 특정 도메인이나 문제 영역과 관련된 규칙, 절차, 정책 등을 구현한 코드
    • 애플리케이션의 핵심 기능을 담당하며, 사용자 요구사항을 충족시키기 위해 데이터를 처리하고 조작하는 방법을 정의한다.
    • 역할
      • 데이터 처리 및 변환 : 데이터의 유효성을 검증하고 필요한 경우 데이터를 변환하거나 계산
      • 비즈니스 규칙 적용 : 특정 도메인과 관련된 규칙을 코드로 구현하여 일관되게 적용한다.
      • 데이터베이스 연산 중재
      • 비즈니스 프로세스 관리
    • 소프트웨어의 핵심 부분으로, 애플리케이션의 기능적 요구사항을 충족시키기 위해 반드시 명확하고 일관되게 구현되어야 한다.
    • 시스템의 유지보수성과 확장성을 높이고, 사용자 요구사항을 효과적으로 반영할 수 있다.
  • DTO
    • Data Transfer Object는 소프트웨어 설계 패턴 중 하나로, 애플리케이션의 서로 다른 계층 간 데이터를 전송하기 위해 사용되는 객체이다.
    • 주로 데이터의 전송에만 사용되며, 비즈리스 로직이나 데이터 접근 로직이 포함되지 않는 단순한 객체이다.
    • 계층 간의 의존성을 줄이고, 데이터 전송을 간편하게 한다.
    • DTO는 직렬화되어 네트워크를 통해 전송되거나, 파일에 저장될 수 있다.
    • 필요한 데이터만 포함하도록 DTO를 설계함으로써, 민감한 데이터가 불필요하게 전송되는 것을 방지할 수 있다.
    • 코드의 복잡성이 증가하고 변환 작업이 필요하다는 단점 존재

Node.js 설치

https://nodejs.org/en/download

 

Node.js — Download Node.js®

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

요 사이트에서 다운로드 하고, 기본 상태 그대로 쭉 설치를 하면 된다.

 

다운로드가 완료되면, 아래 명령어를 사용하여

cmd/터미널 등엑서 node와 npm이 잘 설치되었는 지 확인할 수 있다.

node -v   // 설치된 노드 버전 확인
npm  -v   // 노드 패키지 매니저 버전 확인

나는

v18.20.2

10.5.0

이 떴다 :>

 

 


 

Node.js 세팅 (VS Code)

 

VS Code를 켜고, 작업 폴더로 사용하고 싶은 곳의 경로로 터미널에 접속한다.

 

그 경로에 package.json 생성을 위해 명령어를 입력해준다.

npm init

계속 엔터치면, 아래와 같이 뜬다.

 

 

위 사진에서,

"main" : "index.js" 라고 되어 있는 부분이 서버를 실행시킬 메인 파일이라는 뜻이다.

기본 값이 index.js로 되어 있고, package.json 파일에서 수정할 수 있다.

 

그 이후, 작업 디렉토리를 보면 package.json 이라는 파일이 생성되어 있을텐데,

이 파일은 프로젝트 정보와 의존성을 관리하는 문서이다.

npm, yarn 등을 통해 설치한 모듈이 무엇인지, 버전은 무엇인지 등을 알 수 있다.

 

모듈 설치 시 npm, yarn, pnpm 무엇을 사용하든 상관없다.

 

yarn 설치

npm install -g yarn
yarn -v

위 코드로 깔고 잘 깔렸는 지 버전 체크해주면 된다.

첫줄을 입력했더니 오류가 떠서 

앞에 sudo를 붙여주었더니 설치가 잘 되었다.

 

 


Express

Node.js 웹 애플리케이션 프레임워크이다.

https://expressjs.com/ko/

 

Express - Node.js 웹 애플리케이션 프레임워크

Node.js를 위한 빠르고 개방적인 간결한 웹 프레임워크 $ npm install express --save

expressjs.com

 

npm install express --save   // npm 사용 시
yarn add express             // yarn 사용 시

 

설치한 후, package.json 을 확인해보면, 아래와 같이 dependencies 부분에 express가 추가된 것을 확인할 수 있다.

 

실행시킬 때 필요한 모듈들이 로컬 컴퓨터에 깔려있지 않더라도, 명령어만 치면

package.json에 명시된 내용을 보고 다시 모듈들을 설치할 수 있다.

 

그래서 프로젝트를 github에 올릴 때, .gitignore 부분에 node_modules/가 들어가있는 것 !

 

 

Express 초기 파일 생성

 

먼저 작업 파일 속에 index.js를 만든다.

 

아래와 같이 express 공식 사이트에 예제가 존재하는데,

이 코드를 복사해서 index.js에 넣는다.

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

 

코드를 넣고 그대로 실행시키면 잘 된다.

하지만, 예제에서는 맨 첫번째 줄에서 express를 불어올 때, common JS 방식을 사용한다.

요즘은 ES6 방식 (import로 불러오는 방식)을 주로 사용하니 약간 수정을 해준다.

// const express = require('express')  -> common JS
import express from 'express'        // ES6
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

 

아래 명령어를 쳐서 실행시킨다.

node index.js

 

 

해보면 ?

에러가 뜬다 :>

 

Common JS가 아닌, ES 방식은 별도의 처리가 필요하다.

이 처리를 하기 위해, 나는 Babel을 사용할 것이다.

 

ES6 이상의 최신 문법을 이전 문법으로 변경하여 원활히 동작할 수 있도록 하는

JavaScript 컴파일러라고 생각하면 된다.

 

 

Babel 설치

먼저 패키지를 다운 받는다.

-dev 또는 --dev의 경우, 개발할 때는 필요하지만 실제 릴리즈 시 필요하지 않은 모듈을 사용할 때 사용하는 option 이다.

npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
yarn add @babel/core @babel/cli @babel/preset-env @babel/node --dev

두 방식 중 하나를 선택하여 명령어를 친다.

 

이렇게 되면 package.json 파일의 devDependencies에 babel이 추가된 것을 확인할 수 있다.

 

명령어가 길기도 하고, 반복적으로 수행해야 하는 스크립트를 등록할 때는 scripts에 추가하면 된다.

이렇게 하면 npm run [명령]으로 훨씬 간편하게 사용할 수 있다.

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "babel-node index.js"  // 명령어 길게 치기 귀찮아..ㅜ
  },

 

 

실행시키기 with Babel

모든 세팅이 끝났으니 실행시켜보쟈.

// scripts 설정 안해도 아래 명령어로 실행 가능하다.
npx babel-node index.js

// scripts 설정 시
npm run start
yarn start

 

 

바벨을 깔고 아무리 시도해봐도 에러가 떴다.

오류 메시지를 보면 여전히 ES 모듈을 로드할 수 없다고 떴다.

babel-node가 ES 모듈을 처리하는 데 문제가 있다는 것이다.

버전 확인으로 바벨이 잘 깔린 것까지 확인했는데 ㅜ

 

구글링 해보고 잘 찾아보니까

'.babelrc' 또는 'babel.config.json' 파일을 수정하여

Babel이 ES 모듈을 올바르게 처리하도록 설정해야 했다.

 

'.babelrc' 파일을 생성 또는 수정하여

ES 모듈을 CommonJS 모듈로 변환하는 설정을 추가하였다.

나는 파일이 없길래 프로젝트 루트 디렉토리에 생성하였다.

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    "@babel/plugin-transform-modules-commonjs"
  ]
}

 

이 코드를 추가하여서 다시 yarn start를 실행하였더니 문제가 해결되었다..(ㅜ휴 다행..)

 

브라우저의 localhost:3000에 들어가면

 

 

Nodemon

더욱 편하게 해줄 모듈

저장하고 갱신이 될 때마다, 노드도 갱신시켜준다.

npm install --save-dev nodemon
yarn add nodemon --dev

 

둘 중 하나 입력하여 설치

 

package.json의 scripts 추가하기

 

"start": "nodemon --exec babel-node index.js"

 

 

 

 

실행시키기 with Nodemon, Babel

 

'yarn start'로 실행시키면 요렇게 뜬다

저장하면 다시 시작해주는 아주 아주 편리한 모듈 !

ERD를 설계한 후에 필요한 것은 API를 큰 틀에서 설계하는 것이다.

API의 세부적인 사항을 처음에 다 정하기는 어렵지만, 데이터베이스와 마찬가지로 큰 틀을 잡아두는 것은 가능하다.

 

API 설계 시 필요한 것

1. API 엔드포인트 설계

2. 요청 데이터, 응답 데이터 설계

이 정보들을 문서화해서 프론트 개발자가 API를 사용하기 쉽도록 돕는 문서를 API 명세서라고 한다.

 

 

API란 ? 

Application Programming Interface

쉽게 말해서 어려운 것은 감추고 보다 쉽게 상호작용할 수 있도록 해주는 것

애플리케이션을 프로그래밍할 때, 보다 쉽게 할 수 있도록 해주는 도구들

 

 

REST API

REST는 Representational State Transfer의 약자이다.

HTTP를 기반으로 하는 웹 서비스 아키텍처를 의미하며

HTTP 메소드와 자원을 이용해 서로 간의 통신을 주고받는 방법이다.

 

 

API Endpoint

REST API에서 API Endpoint는 해당 API를 호출하기 위한 HTTP 메소드, URL을 포함한다.

 

 

HTTP 메소드

REST 방식으로 통신할 때 필요한 작업을 표시하는 방법

아래 5가지 메소드는 CRUD(생성, 조회, 갱신, 삭제) 4가지에 대응된다.

1. GET : 조회
2. POST : 생성
3. PUT : 갱신(전체)
4. PATCH :갱신(일부)
5. DELETE : 삭제

POST는 새로운 자원의 생성도 있지만,

클라이언트가 특정 정보를 서버로 넘기고 그에 대한 처리를 요청하는 것을 전부 POST로 처리 가능하다.

 


RESTful API Endpoint의 설계

아래의 규칙에 따라 설계가 가능하다.

 

1. URI에 동사가 포함되면 안된다.

2. URI에서 단어의 구분이 필요한 경우 -(하이픈)을 이용한다.

3. 자원을 기본적으로 복수형으로 표현한다.

4. 단 하나의 자원을 명시적으로 표현하기 위해서는 /users/id와 같이 식별 값을 추가로 사용한다.

5. 자원 간 연관 관계가 있을 경우 이를 URI에 표현한다.

 

 

회원 가입, 로그인, 탈퇴

1. 로그인

로그인의 주체가 되는 대상은 사용자이므로

url 상에 users가 들어가는 것은 쉽게 감이 온다.

 

우선 클라이언트가 로그인에 필요한 정보를 서버로 넘겨 로그인에 대한 처리를 요청하는 것이니

POST로 설계를 해야 한다.

 

회원 가입 역시 새로운 사용자의 생성이기 때문에 POST /users 로 표현이 가능하다.

따라서 회원 가입의 경우가 있기 때문에

로그인을 POST /users로 표현하기 힘들다.

이런 경우에는 너무 타이트하게 RESTful한 설계를 따르려고 하지 않아도 된다.

 

POST /users/login -> 이정도면 괜찮다.

위처럼 HTTP 메소드, 필요한 자원에 대한 명시를 한 URI를 합쳐 API Endpoint라고 한다.

 

웹 서비스를 하는 서버의 도메인 주소가 https://claire.com 일 경우 

POST https://claire.com/users/login 이렇게 요청을 할 경우 로그인이 되는 것이다. 

 

 

 

2. 회원 가입

POST /users 로 해도 되지만

기능 요구 사항에서 회원 가입 외에 사용자가 새로 추가가 되는 경우가 있다면,

해당 API와 구분을 해야 한다.

 

Q. 회원 가입은 유저 한 명이 생기는 건데, POST /users/id 아닌가요 ?

POST /users/id의 형태로 설계를 하려면, 해당 유저를 식별할 값이 존재해야 하는데

회원 가입 이후에서야 해당 유저가 생성이 되기에, 유저를 식별할 값이 API 호출 시점에는 없다.

따라서 이런 경우는 위와 같은 설계가 불가능하다.

 

 

 

3. 회원 탈퇴

DELETE /users 로 설계가 가능하다고 생각이 들지만 !

요구 사항이 회원 탈퇴 시 곧바로 데이터베이스에서 삭제가 아니라, 비활성 계정으로 만들고, 추후 계정 복구를 고려한다면

DELETE로 설계하면 안된다.

 

이런 경우 데이터베이스에서 사용자 테이블의 status를 active에서 inactive로 변경하는 것이기에

일부 수정인 PATCH를 활용해야 한다.

 

PATCH /users

그러나 이 경우 회원 정보 수정 API까지 고려해서 설계를 해야한다.

회원 정보 수정 API 역시 닉네임 등 일부 정보만 수정하는 것이기 때문에

PATCH를 사용하며, PATCH /users로 설계가 가능하다.

 

이런 경우,

회원 정보 수정은 PATCH /users/id 로 특정 유저의 정보 수정임을 명시적으로 표현하고,

회원 탈퇴는 PATCH /users 로 표현할 수 있다.

 

 

 

이렇듯, API 설계에는 절대적인 정답은 없고

위에서 나열한 5개의 규칙을 지키되,

요구 사항에 따라 피치 못한 경우는 무조건 위 규칙을 따르지 않아도 된다.

결국 프론트엔드 개발자분들이 API 설계를 보고 쉽게 무슨 API인지 이해하면 되는 것!

 


 

 

리소스 간 연관 관계가 있는 경우

예를 들어

교과목이 한 명의 사용자(교사)가 여러 개의 교과목을 강의할 수 있다고 하고,

교사와 교과목이 1 : N 관계인 경우

이런 상황에서 교과목의 목록을 조회하는 API를 설계한다고 가정

 

교과목은 교사와 1 : N 관계를 가지므로 아래처럼 설계할 수 있다.

편의를 위해 사용자는 오로지 교사만 있다고 가정

/users/subjects

 

이런 식으로 교사가 여러 개의 과목을 강의하기에

계층 관계는 교사 다음 교과목으로 잡고,

uri 상에 교사가 계층 관계 상 더 우선이 된다는 것을 표현할 수 있다.

 

 

만약, 특정 교사의 교과목 목록을 보고 싶다면?

/users/id/subjects

로 설계가 가능하다.

 

교과목 하나 단건 조회 API를 설계한다면 ?

특정 교사의 특정 교과목이 더 의미가 잘 전달이 된다    ->     /users/id/subjects/id

그저 특정 교과목이 더 의미가 잘 전달이 된다     ->     /users/subjects/id

 

N : M 관계

게시글과 해시태그가 예시이다.

이런 경우 계층 관계를 한 눈에 보기가 힘들다.

비즈니스 로직상 더 중요한 대상을 계층 관계에서 앞에 두는 방법이 있다.

게시글과 해시태그 중 직감적으로 해시태그가 부수적이고, 게시글이 서비스에서 더 중요한 역할을 한다는 것을 알 수 있다.

/articles/hash-tag

 

이렇게 설계가 가능하다.

 

이처럼 API 설계에서도 기획의 요구 사항이 영향을 끼치므로

반드시 PM, 프론트 개발자들과 많은 소통을 하여 설계를 하는 것이 좋다.

 


세부적인 API 설계

API 설계를 할 때 고려해야 할 것들

1. path variable

2. query string

3. request body

4. request header

 

위 4개 중 API endpoint에 포함이 되는 것은 1번 뿐이다.

 

API Endpoint는 간략하게 ( 이런 역할을 하는 API이다 ) 정도만 알려주는 것이다.

 

Q. 회원 가입이란, 회원 정보를 새로 저장하는 건데, 무슨 정보를 저장하나요 ?

엔드 포인트 하나만으로 실제 동작 수행에 필요한 정보를 표현할 수 없다.

이런 경우 바로 위의 4가지를 사용하게 된다.

 

Path Variable

게시글 하나 상세 조회 API 를 예로 들었을 때,

곧바로 단건 조회, 즉 GET에 단건 조회라는 것은 빠르게 알 수 있다.

 

여기서 어떻게 특정 게시글임을 서버에게 전달이 가능할까 ?

어떻게 서버에게 원하는 게시글을 식별할 수 있는 데이터를 넘길 수 있을까 ?

 

이런 경우 path variable을 사용한다.

단 하나, 특정 대상을 지목할 때

/users/articles/id 처럼 id에 게시글의 식별 값을 넣어서 전달하게 된다.

 

GET https://claire.com/users/articles/4

여기서 4가 데이터베이스 상에서 게시글의 기본키이다.

 

 

실제 API 명세서에서는 아래처럼 표현한다.

GET /users/articles/{articleId}

 

{ } (중괄호)로 감싸는 부분은 path variable을 의미하고,

그냥 id 보다는 articleId처럼 더 명확하게 표시해주는 것이 좋다.

 

 

단어 구분을 -를 사용해서 한다고 했으니,

GET /users/articles/{article-id}

로 해도 된다.

 

 

Query String

게시글 중 이름에 claire가 포함된 게시글들을 조회하려고 할 때,

1개가 될 수도 있고 여러 개가 될 수도 있다.

 

따라서 단 하나만 조회하는 path variable을 사용하는 것은 아니고,

query string을 활용하는 것이다.

 

쿼리 스트링은 보통 검색 조회 때 사용이 되며 따라서 GET 요청에서 사용이 된다.

GET /uesrs/articles?name=claire

위처럼 name이 claire라는 것은 전달 할수도 있고,

 

전달할 정보가 여러 개일 경우 아래와 같이

GET /users/articles?name=claire&group=umc

&를 통해 쿼리 스트링으로 전달하려는 값을 여러 개 연결할 수도 있다.

 

주의할 점은, 쿼리 스트링은 API 엔드 포인트에 포함이 되지 않기 때문에

엔드 포인트 자체는 GET /users/articles 로 설계해야 한다.

 

 

 

Request Body

POST는 조회가 아닌, 생성을 위해 사용하기도 하기 때문에

URL에 해당 정보들을 노출하는 것은 굉장히 위험할 수 있다.

 

따라서 URL에 노출이 되지 않고, request body에 해당 데이터를 담을 수 있으며,

보통 json의 형태 혹은 form-data 형태로 보내게 된다.

 

예를 들어 회원 가입시 아래의 정보가 필요하다고 할 때,

1. 이름

2. 전화번호

3. 닉네임

 

request body에 위의 정보를 json으로 담아서 서버로 전송하게 된다.

{
	"name" : "클레어",
    "phoneNum" : "010-1111-2222",
    "nickName" : "claire"
}

 

 

Request Header

서버에 전송 시 메타데이터,

즉 전송에 관련된 기타 정보들이 담기는 부분이다.

 

body에 담기는 데이터의 형식이 json인지, 아니면 form-data인지를 담기도 하고

혹은 데이터를 담을 수도 있다.

 

대표적으로 로그인이 되었다는 것을 알려주기 위한 토큰을 헤더에 담기도 한다.

 

 

 

 


API 설계 예시

닉네임 변경 ( 사용자 정보 변경 )  API에 대한 설계

 

API Endpoint

PATCH /users/{userId}

 

 

Request Body

{
	"nickname : claire"
}

 

 

Request Header

Authorization : accessToken (String)

 

accessToken은 로그인 된 사용자가 나 로그인된 상태야 ! 하고 알려주는 것으로

보통 Authorization이라는 키에 대한 값으로 헤더에 담아서 서버로 보낸다.

 

 

Query String

필요 없다.

본 글은 UMC EWHA에서 활동하며 공부하는 워크북을 토대로 정리된 글입니다.

 

 

 

query 역시 데이터베이스 설계처럼 정해진 답이 없고,

join을 사용할 지 혹은 subquery를 사용할 지 선택하면 된다.

무리해서 한번에 큰 query를 보내기 보다는 2개로 쪼개서 보내는 것도 좋다.

 

1. 책이 받은 좋아요 개수를 보여주기

likes 칼럼 없이 집계

select count(*) from book_likes where book_id = {대상 책 아이디};

 

여기에 추가적으로 내가 차단한 사용자의 좋아요는 집계를 하지 않도록 설계

select count(*) from book_likes where book_id = 3 and 
	user_id not in (select target_id from block where owner_id = 2);

 

위의 커리를 inner join으로 구현

select count(*)
from book_like as bl
inner join block as b on bl.user_id = b.target_id and b.owner_id = 2
where bl.book_id = 3;

 

혹은 left join

select count(*)
from book_like as bl
left joinblock as b on bl.user_id = b.target_id and b.owner_id = 2
where bl.book_id = 3 and b.target_id is null;

 

+ join 연산 보다는 sub query가 더 가독성이 좋다.

 

2. 해시태그를 통한 책의 검색

쿼리를 작성할 때 N:M 관계로 인해 사이에 매핑 테이블이 추가된 경우는 쉬운 쿼리로 데이터를 가져오기가 힘들다.

 

select * from book where id in 
	(select book_id from book_hash_tag
    	where hash_tag_id = (select id from hash_tag where name = 'UMC'));

 

join 연산으로

select b.*
from book as b
inner join book_hash_tag as bht on b.id = bht.book_id
inner join hash_tag as ht on bht.hash_tag_id = ht.id
where ht.name = 'UMC';

 

 

책들의 목록을 최신 순으로 조회하는 쿼리

select * from book order by created_at desc;

 

좋아요 개수 순으로 목록 조회

book 자체에 likes 칼럼 없음 -> 단순 select로 조회 불가

select * from book as b
join (select count(*) as like_count from book_likes group by book_id)
	as likes on b.id = likes.book_id
order by likes.like_count desc;

 

 

3. 페이징

데이터베이스 자체에서 끊어서 가져오는 것이 필요하고 이를 paging 이라고 한다.

paging에는 2가지 종류가 있다.

 

1. Offset based 페이징

우리가 자주 접하는 페이징

직접 페이지 번호를 찾아내어 이동하는 페이징

paging 쿼리는 sql마다 다르며 아래 코드는 mysql 기반으로 한다.

 

select*
from book
order by likes desc
limit 10 offset 0;

 

limit을 통해 한 페이지에서 보여줄 데이터의 개수를 정하고

offset으로 몇 개를 건너뛸지를 정한다.

 

페이지 x번에 대하여 한 페이지에 y개를 보여준다면

select *
from book
order by likes desc
limit y offset (x-1)*y;

 

보통 1페이지가 첫 페이지 때문에 ( x-1 ) 이다.

 

페이지 n 번에 대해 한번에 15 개씩 보여준다.

+ 인기 순 정렬

select * from book as b
join (select count(*) as like_count from book_likes group by book_id)
	as likes on b.id = likes.book_id
order by likes.like_count desc
limit 15 offset (n-1) * 15;

 

Offset paging의 단점

: 직접 여러 개의 데이터를 넘어가서 가져오는 개념이라 페이지가 뒤로 갈수록 넘어가는 데이터가 많아져

  성능 상의 이슈가 있고, 결정적으로 아래와 같은 문제 상황이 발생할 수 있다.

 

사용자가 1페이지에서 2페이지로 넘어가려는 찰나, 게시글 6개가 추가 되었다.

-> 분명 2 페이지로 왔는데 1페이지에서 보았던 게 또 보임

이런 단점을 보완한 페이징 기법이,

 

2. Cursor based 페이징

여기서 커서는 마지막으로 조회한 컨텐츠이다.

= 마지막으로 조회한 대상 그 다음부터 가져오기

 

마지막으로 조회한 책의 좋아요가 20이라면

select * from book where book.likes <
	(select likes from book where id = 4)
order by likes desc
limit 15;

 

책 목록 조회 쿼리

select * from book where created_at <
	( select created_at from book where id = 3)
order by created_at desc
limit 15;

 

인기순

 

select * from book as b
join (select count(*) as like_count from book_likes group by book_id) as likes
	on b.id = likes.book_id
where likes.like_count < (select count(*) from book_likes where book_id = 3)
order by likes.like_count desc
limit 15;

 

위의 인기순 커서 페이징 쿼리는 제대로 작동하지 못한다.

왜냐하면 예를 들어, 좋아요가 0인 게시글이 400개이고, 마지막으로 조회한 책의 좋아요가 0개라면,

그리고 아직 뒤에 조회를 하지 않은 책이 있다면

다음 페이지에 책이 목록으로 조회가 되지 않는다.

 

따라서 인기순 정렬 같이 같은 값이 있을 수 있는 경우 정렬 기준이 하나 더 있어야 한다.

위의 코드에서 좋아요 수가 같을 경우 최신 순으로 정렬이 되도록

select * from book as b
join (select count(*) as like_count from book_likes group by book_id) as likes
	on b.id = likes.book_id
where likes.like_count < (select count(*) from book_likes where book_id = 3)
order by likes.like_count desc, b.cread_at desc
limit 15;

 

4. 책 목록 조회 시 좋아요 순으로 커서 페이징 + 차단한 유저의 좋아요는 집계 X

select b.*
from book as b
join (
	select bl.book_id, count(*) as like_count 
    from book_likes as bl
    where not exists (
    	select target_id
        from block as bc
        where bc.target_id = bl.user.id and bc.owner_id = 3
        )
     group by bl.book_id
 ) as likes on b.id = likes.book_iud
order by likes.like_count desc, b.created_at desc
limit 15;

 

 


 

Join 연산

  • MySQL에서 join 연산은 두 개 이상의 테이블에서 데이터를 결합하는 데 사용된다.
  • 주로 관계형 데이터베이스에서 테이블 간의 관계를 나타내기 위해 사용된다.
  • 여러 테이블 간의 관계를 이해하고 데이터를 효율적으로 결합하여 필요한 정보를 추출하는 데 사용된다.
  • Join 연산의 종류
    • INNER JOIN : 두 테이블 간의 일치하는 행만 반환한다. 즉, 조인 조건을 충족하는 행들만 선택된다.
    • LEFT JOIN : 왼쪽 테이블의 모든 행과 오른쪽 테이블에서 일치하는 행을 반환한다. 오른쪽 테이블에서 일치하는 행이 없는 경우에도 왼쪽 테이블의 행은 반환된다. 만약 오른쪽 테이블에서 일치하는 행이 없으면 오른쪽 테이블의 모든 열은 NULL 로 채워진다.
    • RIGHT JOIN : LEFT JOIN의 정반대로 작동한다. 즉 오른쪽 테이블의 모든 행과 왼쪽 테이블에서 일치하는 행을 반환한다.
    • FULL JOIN : 두 테이블 중 어느 한쪽에도 일치하지 않는 모든 행을 반환한다. 즉, 왼쪽 테이블과 오른쪽 테이블의 모든 행을 반환하고 일치하는 행이 없는 경우에는 NULL 로 채운다.
    • CROSS JOIN ( 상호 조인 ) : 한쪽 테이블의 모든 행과 다른 쪽 테이블의 모든 행을 조인시킨다. 상호 조인 결과의 전체 행 개수는 테이블의 각 행의 개수를 곱한 수이다.
    • SELF JOIN ( 자체 조인 ) : 자기 자신과 조인하므로 1개의 테이블을 사용한다.
  • 일반적으로는 INNER JOIN이 가장 많이 사용되지만, 데이터의 특성과 요구사항에 따라 다른 조인 유형을 사용해야 할 수도 있다.

본 글은 UMC EWHA에서 활동하며 공부하는 워크북을 토대로 정리된 글입니다.

 

Web Server Web application Server가 따로 존재한다.

초기 인터넷에서는 Web application Server가 필요 없었다.사용자가 10명이라면 html 페이지를 10개 만들면 되었던..

 

미리 정해진(정적인) 콘텐츠를 준비해두고 요청이 오면 응답으로 주는 것이 아닌,요청이 올 때마다 해당 요청에 적절한 콘텐츠를 만들 수 있게(동적인) 필요한 것이

Web application Server

: 클라이언트의 요청에 대해 적절한 데이터를 만들어주는 서버

: 우리 주변의 Web application Server는 Node.js , SpringBoot가 있다.

 

Web Server 정적 콘텐츠 호스팅

NGINX가 요청에 대해 정적 콘텐츠(미리 정해진 html)를 준 것.

NGINX에서는 설정 파일이 있고, 웹 서버가 실행 될 때 이 설정 파일을 읽으면서 실행이 된다.

*ubuntu 기준, /etc/nginx/sites-available 디렉토리에서 default nginx의 설정 파일

 

설정 파일 중 server block

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        root /var/www/html; <- 중요

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        # pass PHP scripts to FastCGI server
        #
        #location ~ \.php$ {
        #       include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
        #       fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #       deny all;
        #}
}

 

root는 정적 콘텐츠를 찾는 시작 디렉토리를 의미한다.

index는 기본적인 요청에 대해 index 뒤의 파일들을 찾아서 웹 상으로 보여준다는 것

 

root/var/www/html 을 통해 정적 콘텐츠를 찾아낼 시작 디렉토리를 설정하고

index를 통해 기본 요청이 온 경우 어떤 파일을 줄 지 설정하여,

/ 요청에 대해

/var/www/html/index.nginx-debian.thml 을 응답으로 준 것

 

location /y{
	root /x
}

 

이 설정은 /y 로 요청이 오면 /x/y에서 파일을 찾아라 라는 뜻

 

Web Server VS Web Application Server(WAS)

WAS는 어떻게 Web Server와 함께 동작이 될까 ?

Node.js, Spring Boot 모두 WAS이며 동작을 시키면 WAS가 동작이 된다.

 

네이버가 WAS 하나만 뒀을 경우 www.naver.com:8080  으로 접속해야만 한다.

우리는 실제로 www.naver.com  이렇게만 접속-> DNS 를 통해 아이피 주소로 변환이 되기 때문 -> 따라서 <네이버 서버 컴퓨터 아이피주소:웹 서버의 프로세스가 부여 받은 포트번호> 이렇게 요청을 보내게 됨

 

Reverse Proxy

클라이언트와 서버 간의 통신을 중계하고 보안, 성능 개선 등의 목적을 위해 중간에 위치하는 서버프록시 서버 자체는 대리자로써 우선 클라이언트의 요청을 받고 본 서버로 보내준다.

 

서버로써 요청을 받기만 하지 않는다.서버 프로세스가 내부적으로 connect() 시스템 콜과 같은 요청을 보내는 시스템 콜을 통해다른 서버 프로세스에게 다시 요청을 보낼 수 있다.

 

포워드 프록시 vs 리버스 프록시포워드 프록시는 또 다른 외부 (다른 컴퓨터) 서버로 요청을 보내주고,리버스 프록시는 내부 (같은 컴퓨터)의 다른 서버로 요청을 보내준다.


 

Web Sever

- 웹 서버는 인터넷을 통해 웹 페이지, 이미지, 동영상 등의 콘텐츠를 사용자에게 제공하는 소프트웨어이다.

- 웹 서버는 클라이언트의 요청을 받아들이고, 요청된 콘텐츠를 전송하여 사용자가 웹 페이지를 볼 수 있도록 한다.

- 웹 서버의 주요 기능

  1. HTTP 요청 처리 : 클라이언트로부터 HTTP 요청을 받아들인다. 이 요청은 주로 웹 브라우저를 통해 이루어지며, 클라이언트가 원하는 웹 페이지의 URL과 함께 전달된다.
  2. 정적 콘텐츠 제공 : 웹 서버는 정적인 콘텐츠를 제공한다. 이는 주로 html 파일, 이미지, 스타일 시트, 자바스크립트 파일 등을 포함한다. 이러한 콘텐츠는 웹 서버의 파일 시스템에 저장되어 있다.
  3. 동적 콘텐츠 생성 : 일부 웹 서버는 동적인 콘텐츠를 생성할 수도 있다. 이를 위해 웹 서버는 요청된 정보를 처리하여 새로운 html 페이지를 생성하거나, 데이터베이스에서 정보를 추출하여 사용자에게 동적으로 제공한다.
  4. 세션 관리 : 웹 서버는 클라이언트와의 상호 작용을 기록하고 관리하는 데 사용되는 세션 관리를 제공한다. 사용자 인증, 쿠키 생성 및 관리 등을 수행할 수 있다.
  5. 보안 기능 : 웹 서버는 보안 기능을 제공하여 악의적인 요청으로부터 웹 애플리케이션을 보호한다. 이를 통해 DDoS 공격 방어, SSL/TLS 암호화, 웹 방화벽 등을 구현할 수 있다.
  6. 로깅 및 모니터링 : 웹 서버는 클라이언트 요청과 서버의 응답을 기록하여 로그 파일에 저장한다. 웹 사이트의 성능을 모니터링하고 문제를 해결할 수 있다.

- Apache, Nginx, Microsoft llS 등이 있다. 웹 서버들은 개별적으로 또는 프록시, 로드 밸런싱 등과 함께 사용되어 대규모 웹 애플리케이션을 운영하는 데 활용된다.

 

Web Application Server

- 동적인 웹 애플리케이션을 실행하기 위한 소프트웨어이다.

- 클라이언트의 요청을 받아들이고, 해당 요청을 처리하여 동적으로 생성된 콘텐츠를 클라이언트에게 제공한다.

- 정적인 콘텐츠뿐만 아니라 데이터베이스나 다른 외부 시스템과의 상호작용을 통해 동적인 콘텐츠를 생성하고 전달한다.

- 주요 기능

  1. 웹 애플리케이션 실행 환경 제공 : 일반적으로 서블릿 컨테이너 CGI(Common Gateway Interface) 인터페이스, 또는 특정 프로그래밍 언어의 실행 환경을 포함한다.
  2. 동적 콘텐츠 생성 : 웹 애플리케이션 서버는 웹 애플리케이션의 로직을 실행하고, 데이터베이스나 외부 API와의 상호작용을 통해 필요한 데이터를 처리한다.
  3. 스레드 관리 및 자원 할당 : 다중 사용자 요청을 동시에 처리하기 위해 스레드 풀이나 프로세스 풀 등의 매커니즘을 사용하여 스레드 관리를 수행한다. 서버 자원을 효율적으로 할당하여 성능을 최적화한다.
  4. 세션 관리 : 클라이언트와의 세션을 관리하고 상태를 유지한다. 이를 통해 사용자 인증, 상태 관리, 쇼핑 카트 등의 기능을 구현할 수 있다.
  5. 보안 기능 : 보안을 강화하기 위한 다양한 기능을 제공한다. 인증, 권한 부여, 입력 유효성 검사, 암호화 등을 포함할 수 있다.
  6. 로깅 및 모니터링 : 클라이언트 요청과 서버의 응답을 기록하여 로그 파일에 저장하고, 서버의 상태를 모니터링한다.

- Apache Tomcat, Jetty, IBM WebSphere, Red Hat JBoss 등이 있고, 이러한 서버들은 Java EE와 같은 특정 플랫폼의 웹 어플리케이션을 실행하는 데 사용된다.

 

Reverse Proxy

- 클라이언트 요청을 받아들여 이를 서버로 전달하고, 서버로부터 받은 응답을 클라이언트에게 반환하는 중간 서버

- 클라이언트와 서버 간의 통신을 중재하며 여러 가지 목적으로 사용된다.

- 주요 기능

  1. 로드 밸런싱 : 여러 대의 백엔드 서버로 들어오는 요청을 분산하여 부하를 균형있게 분산시킨다. 단일 서버에 발생하는 과부하를 방지하고 서비스의 가용성과 성능을 향상시킨다.
  2. 보안 : 클라이언트와 서버 사이에 위치하여 보안을 강화할 수 있다. 리버스 프록시는 클라이언트에게 직접적인 서버 정보를 노출하지 않고 서버의 익명화를 제공할 수 있으며, 보안 정책을 적용하여 악의적은 트래픽을 필터링하거나 방화벽 역할을 수행할 수 있다.
  3. 캐싱 : 서버로부터 받은 응답을 캐시하여 동일한 요청이 반복될 때 서버에게 다시 요청하는 대신 캐시된 응답을 바로 제공할 수 있다. 이를 통해 네트워크 대역폭을 절약하고 응답 시간을 단축하여 전반적인 성능을 향상시킬 수 있다.
  4. SSL 오프로딩 : 클라이언트와 서버간의 SSL 연결을 처리하여 서버의 부하를 줄이고 SSL 인증서를 중앙 집중식으로 관리 할 수 있다.
  5. 애플리케이션 관리 및 모니터링 : 리버스 프록시는 애플리케이션의 트래픽을 모니터링하고 관리할 수 있는 기능을 제공한다. 트래픽 패턴을 분석하고 성능 문제를 식별하여 대응할 수 있다.

- Nginx, Apache HTTP Server의 모듈인 mod_proxy, HAProxy, Envoy 등이 있다. 웹 서버와 함께 사용되어 다양한 웹 애플리케이션 아키텍처를 구현하는 데 활용된다.

본 글은 UMC EWHA에서 활동하며 공부하는 워크북을 토대로 정리된 글입니다.

 

ERD는 언제 설계하는 것이 가장 좋은가 ?

프로젝트 시작과 동시에 설계하는 것이 좋다.

ERD의 세세한 내용은 언제든지 바뀔 수 있기 때문에 처음부터 완벽하게 만들 필요는 없다.

실제 기능 구현을 진행하며 필요한 내용을 수정한다.

 

모든 팀원이 인지하는 데이터베이스는 동일해야 한다.

각자 자기 마음대로 데이터베이스를 설계하고, 작업 후 나중에 합치는 행위는 매우 좋지 않다.

처음에 빠르게 ERD를 설계하여, 모두가 공통된 데이터베이스에 대해 인지한 후 작업을 하는 것이 좋다.

 

  1. 테이블 이름과 칼럼 이름은 모두 소문자, 단어 구분은 대소문자보다 밑줄로 구분을 해주는 것이 좋다.
  2. 기본 키를 위해 각 엔티티 정보 중 유일한 값을 기본 키로 설정하기보다는 Index를 따로 두는 것이 편하다.
  3. 기본 키 타입은 int가 아닌 추후 서비스 확장을 고려해서 bigint로
  4. 각각 키 타입은 PM에게 물어봐야 확실히 결정이 가능하다.
  5. datetime(6)은 밀리초 소수점 6자리까지 구분한다는 의미
  6. Hard Delete는 지양하기 -> status나 inactive_date를 따로 두어 비활성 상태로 두고 일정 기간동안 비활성인 경우 자동 삭제가 되도록
  7. batch를 이용하여 soft delete (HTTP Method 중 Patch)
  8. N : M 관계일 때는 가운에데 매핑 테이블을 따로 두기 -> 양쪽의 기본 키를 외래키로 가지고, 각각과 1 : N 관계를 가진다.

 

 

RDS 설정

데이터베이스를 로컬 컴퓨터에 두는 것이 좋을까 ?

당연히 좋지 않다.

컴퓨터를 끄면 데이터베이스 접속이 안되기도 하고,

로컬 컴퓨터에 접속을 한다는 것은 해당 컴퓨터의 데이터베이스로 접속이 되도록

포트포워딩을 해야하기도 하다.

 

따라서 데이터베이스도 EC2처럼 외부 컴퓨터를 빌려서 사용하는 것이 좋다.

RDS를 사용하게 될 경우 더 유연하게 데이터베이스를 사용할 수 있다.


 

외래키

  • 외래키(Foreign Key)는 한 테이블의 필드가 다른 테이블의 기본 키(primary key)를 참조하는 제약 조건이다. 데이터의 무결성과 관련이 있다.
  • 외래키의 주요 목적
    1. 데이터 무결성 보장 : 외래키 제약 조건은 데이터베이스의 무결성을 유지한다. 참조되는 값이 항상 존재함을 보장한다. 따라서 데이터베이스의 부적절한 참조를 허용하지 않는다.
    2. 데이터 일관성 유지 : 데이터베이스 내의 관련된 정보가 서로 일치함을 보장한다.
    3. 데이터 정규화 : 데이터를 적절하게 분리함으로써 중복을 줄이고 데이터베이스의 구조를 효율적으로 유지할 수 있다.
    4. 조인 : 데이터를 연결하고 검색할 때 조인을 수행하는 데에 외래키가 사용된다. 다양한 테이블 간의 관계를 활용하여 필요한 정보를 추출할 수 있다.
    5. 데이터 무결성 유지 및 제어 : 무결성 위반 시 데이터베이스가 적절히 반응할 수 있도록 보장할 수 있다. 데이터베이스는 데이터 무결성을 위반하는 연산을 거부할 수 있다.
    외래키 제약 조건을 잘못 구성하면 데이터 조작 작업에 제한을 받을 수 있으니 주의해야 한다.

 

기본키

  1. 고유성 : 기본키는 각 레코드를 고유하게 식별한다. 동일한 값을 가진 두 개 이상의 레코드를 가질 수 없다.
  2. NULL 값 제한 : 기본키는 NULL 값을 가질 수 없다. 각 레코드는 기본키 필드에 유효한 값을 가져야 한다.
  3. 데이터베이스 인덱스 생성 : 기본키 필드는 일반적으로 데이터베이스 인덱스를 생성하여 데이터 액세스의 성능을 향상시킨다.
  4. 외래키 참조 : 기본키는 다른 테이블에서 외래키로 참조될 수 있다. 이를 통해 테이블 간의 관계를 설정하고 데이터의 일관성을 유지할 수 있다.

일반적으로 기본키는 테이블에 새로운 레코드를 추가할 때 자동으로 생성된다.

  • 기본키를 선택할 때는 고유성, 애플리케이션 요구사항, 성능 등을 주의해야 한다.
  • 기본키(Primary Key)는 관계형 데이터베이스에서 각 레코드를 고유하게 식별하는 데 사용되는 필드이다. 기본키는 데이터베이스 테이블 내에서 각 행을 식별하는 데 사용되며, 중복된 값을 허용하지 않는다. 데이터베이스의 무결성과 구조를 유지하는 데 중요한 역할을 한다.

 

ER 다이어그램

  • (Entity-Relationship, ER) 다이어그램은 데이터베이스 설계에서 가장 일반적으로 사용되는 도구 중 하나이다. ER 다이어그램을 사용하면 데이터 모델의 구조를 시각적으로 표현할 수 있다.  ER 다이어그램은 개체(Entity), 속성(Attribute), 관계(Relationship) 등의 요소를 사용하여 데이터 모델을 설명한다.
  • ER다이어그램의 요소 
    1. 개체 (Entity)
    2. 실제 세계에서 독립적으로 존재하는 개념
    3. 속성 (Attribute)
      1. 개체의 특성이나 세부 정보
      2. 개체 안에 속성 이름과 속성 유형이 기록된다.
    4. 관계 (Relationship)
      1. 개체 간의 관련성
      2. 관계의 이름과 관계의 유형이 표시된다.
    데이터베이스의 초기 단계에서 ER 다이어그램을 작성하고 검토함으로써 데이터 모델의 설계 결함을 미리 파악하고 수정할 수 있다. 이는 데이터베이스의 효율성과 유지보수성을 향상시키는 데 도움이 된다.

 

복합 키

  • 복합키(Composite Key)는 관계형 데이터베이스에서 한 개체를 식별하는 데 사용되는 두 개 이상의 필드(또는 속성) 조합이다. 기본키(primary key)가 테이블 내에서 각 레코드를 고유하게 식별하는 데 사용되는 단일 필드인 반면, 복합키는 두 개 이상의 필드를 조합하여 고유성을 보장한다.
  1. 고유성 : 복합키는 해당 테이블 내에서 각 레코드를 고유하게 식별한다. 각 필드 조합이 고유한 값을 생성하여 중복을 허용하지 않는다.
  2. 여러 필드 조합 : 복합키는 두 개 이상의 필드 조합으로 이루어진다. 이 필드들은 해당 테이블의 레코드를 식별하는 데 사용된다.
  3. 데이터 무결성 : 복합키를 사용하면 데이터 무결성을 유지할 수 있다. 중복된 값을 방지하고 데이터의 일관성을 유지한다.
  4. 조인 및 검색 : 필요에 따라 데이터를 조인하거나 검색할 때 사용된다.
  • 복합키를 구성할 때 어떤 필드를 선택할 지 신중하게 결정해야 한다.
  • 복합키가 인덱스로 구성되어 데이터 액세스의 성능을 향상시키는 지 확인해야 한다.
  • 필드 조합이 너무 복잡하면 성능에 오히려 부정적일 수 있다.

 

연관관계

  • 연관관계(Relationship)는 관계형 데이터베이스에서 여러 테이블 간의 관계를 설명하는 데 사용되는 개념이다. 데이터의 구조를 이해하고 데이터 간의 상호 작용을 파악하는 데 도움이 된다.
  • 주로 세 가지 유형으로 나뉜다.
    1. 일대일 관계
    • 한 테이블의 레코드가 다른 테이블의 레코드와 하나씩만 연결.
    1. 일대다 관계
    • 한 테이블의 레코드가 다른 테이블의 여러 레코드와 연결.
    1. 다대다 관계
    • 한 테이블의 레코드가 다른 테이블의 여러 레코드와 연결될 수 있으며, 그 반대도 마찬가지.
    • 이러한 관계는 중간 테이블을 사용하여 구현된다. 중간 테이블은 두 테이블 간의 관계를 나타내는 외래키를 포함한다.
    올바른 연관관계를 설정하면 데이터의 일관성과 무결성을 보장할 수 있으며, 데이터를 효율적으로 관리하고 검색할 수 있다. 효과적인 데이터 모델링을 위해서는 각 테이블 간의 연관관계를 명확하게 이해하고 정의해야 한다.

 

정규화

  • 정규화(Normalization)는 데이터베이스 설계에서 중복을 최소화하고 데이터 무결성을 유지하기 위해 데이터를 구조화하는 프로세스이다. 데이터베이스의 효율성과 일관성을 향상시킬 수 있다. 정규화는 테이블을 여러 개의 관계적으로 일관된 형태로 분해함으로써 이루어진다.
    1. 제 1정규화 (1NF) : 테이블 내의 모든 속성이 원자 값만을 가지도록 한다. 즉 속성의 도메인이 원자적이어야 한다. 이렇게 함으로써 중복을 최소화하고 데이터의 일관성을 유지할 수 있다.
    2. 제 2정규화 (2NF) : 모든 비주요 속성이 기본키에 대해 완전 함수 종속되도록 한다. 부분 함수 종속을 제거하여 데이터의 일관성을 유지할 수 있다.
    3. 제 3정규화 (3NF) : 모든 비주요 속성이 후보키에 대해 이행적으로 종속되지 않도록 한다.
    정규화 단계를 거쳐 데이터를 구조화하면 데이터 중복이 최소화되고 데이터베이스의 일관성과 무결성이 향상된다. 경우에 따라서 과도한 정규화는 성능을 저하시킬 수 있으므로, 적절한 정규화 수준을 선택해야 한다.

 

반 정규화

  • 반정규화(Denormalization)는 정규화된 데이터베이스 스키마를 역으로 조정하여 성능 향상을 위해 중복, 불필요한 조인 및 복잡성을 추가하는 프로세스이다.
  • 정규화는 데이터의 중복을 최소화하여 데이터 일관성을 보장하는 데 중점을 두는 반면, 반정규화는 성능을 향상시키기 위해 데이터 중복을 허용할 수 있다.
  • 반정규화의 주요 목적
    1. 읽기 성능 향상 : 정규화된 스키마는 읽기 작업에 대해 많은 조인을 필요로 할 수 있으며 이는 성능을 저하시킬 수 있음. 반정규화된 스키마는 읽기 작업을 더 효율적으로 수행할 수 있다.
    2. 복잡성 감소 : 조인을 최소화하고 중복 데이터를 허용함으로써 쿼리 작성과 유지보수를 간소화할 수 있다.
    3. 쿼리 최적화 : 반정규화된 스키마는 특정 쿼리나 비즈니스 요구사항에 맞춰 최적화되어 있을 수 있다.
  • 주요한 읽기 작업이 많은 경우나 데이터베이스의 크기가 작고 변경 빈도가 낮은 경우 반정규화가 도움이 될 수 있다.
  • 반면 반정규화를 사용하면 데이터 중복이 증가하므로 데이터의 무결성과 일관성을 저해할 수 있다. 쓰기 작업이 많은 경우 데이터 일관성을 보장하기 위해 추가적인 관리가 필요할 수 있다.

+ Recent posts