본문 바로가기
Node.js

Node.js & 익스프레스 사용 정리 (WAS 구현)

by SuldenLion 2023. 7. 27.
반응형

라우터에 대해 알아보고 Node.js와 Express를 사용하여 짠 프로그램들을 정리해 보겠다.

박승규 저자의 Node.js 백엔드 개발자 되기 책의 내용을 참고하였다.

 

 

라우터(Router)란?

- 라우터는 네트워크에서 데이터를 한 곳에서 다른 곳으로 전달하는 장비 또는 소프트웨어

- 라우터는 인터넷이나 로컬 네트워크에서 컴퓨터, 스마트폰, 서버 등의 장치들 사이에서 데이터를 전송하는 역할을 수행함.

- 데이터 패킷을 이동시키고 목적지까지 최적의 경로를 결정하는 역할을 함.

- 패킷을 분석하여 목적지 주소를 확인하고, 다른 네트워크나 서브넷으로 패킷을 전송함. 이때, 최적의 경로를 선택하여 데이터 전송을 빠르고 안정적으로 이루어지게함.

- 라우터는 여러 개의 인터페이스를 가지고 다른 네트워크와 연결되며, 이러한 인터페이스들을 통해 패킷을 주고받음.

- 인터넷의 경우, 전 세계에 분산된 수많은 라우터들이 연결되어 있으며, 이들이 함께 동작하여 인터넷 데이터의 전송과 라우팅을 담당함. 로컬 네트워크에서도 라우터는 서브넷이나 VLAN(Virtual Local Area Network)을 구분하고, 네트워크간 통신을 관리하는 역할을 수행함.

- 일반적인 웹 서버는 URL 경로에 따라서 다른 응답을 줌(= 라우팅)

- 또한, 라우터는 보안을 위한 방화벽 기능이나 네트워크 주소 변환(NAT, Network Address Translation) 등을 지원하여 네트워크의 보안과 효율성을 높이는 역할도 함.

 

- Node.js에서 url이라는 모듈을 사용하면 URL 경로를 간단하게 읽을 수 있음.

 

> URL 구조

http://www.example.com:80/path/to/file.html?key1=value#hash

http => 프로토콜

www.example.com => hostname(도메인명) 

80 => 포트 번호

path/to/file.html => pathname(웹 서버 자원에 대한 경로)

?key1=value => query string(웹 서버에서 제공하는 추가 매개변수)

#hash => hash(서버에 전송하지 않는 일종의 북마크)

 

+) 쿼리 스트링 = HTTP 요청을 보낼때 사용자가 원하는 값을 보내는 방식. 경로값 뒤에 ?를 붙인다음 key=value 형식으로 사용가능. 여러개일 경우 '&'로 구분해서 추가

 

pathname에 있는 정보를 활용하여 라우터 만들기

- localhost:3000/user와 localhost:3000/feed라는 두개의 URL이 있다고 가정. 두 요청에 대해 다른 응답을 줘보겠다.

 

 

http 모듈로 서버 인스턴스를 만들고, url 모듈을 사용해 요청(req)으로 받은 url의 pathname을 얻어낸다.

url이 "localhost:3000/user"라면 pathname으로 "/user"를 가져온다.

 

조건문에서는 받아온 pathname이 "/user"이냐, "/feed"이냐, 혹은 그 외의 것이냐로 나누어 결과를 세팅해보겠다.

 

pathname: /user 일 때

 

pathname: /feed 일 때

 

그 외의 경로일 때

 

각각 위와 같은 결과가 나온다.

url모듈과 if문만 사용한 간단한 라우터이다. (실제로 프로덕션 레벨에서 사용하는 라우터는 복잡함. 하지만 요청으로 들어온 정보를 분석해서 라우팅하는 방법 자체는 같음)

 

 

위 코드(createServer) 리펙터링하기

- 위의 코드는 요청에 대한 응답을 createServer() 안에서 직접 컨트롤하는데, 이는 createServer() 콜백 함수에 모든 코드를 추가해야하므로 좋지 않음. 

- 라우터와 실행하는 함수 부분을 나누어 보겠다. 

 

조건문 내부의 내용을 함수로 만들어 처리했다. → 메인 루틴을 깔끔히 유지하면서도 요청별 함수만 요구사항에 알맞게 변경하면 되는 코드가 됨. 

 

 

동적 응답하기

"localhost:3000/user"에 들어갔을때, 고정된 값이 아닌 동적으로 변경되는 응답이 보여지도록 해보겠다.

 

user 함수를 다음과 같이 바꿔준다.

(주의할점은 end에 넣을 문장에서 큰따옴표가 아닌 그레이브 기호( ` )로 작성한다.)

 

url 쿼리 스트링으로 name과 age 정보 추가

 

 

 

▷ 라우터 리펙터링하기

- 위의 코드는 if문에서 모든 요청을 분석한다. (함수가 user(), feed(), notFound() 뿐만 아니라 수백개가 넘어간다면 유지보수 문제 생김)

- 함수를 추가할 떄마다 if문에서 실수하지 않도록 조치해야함.

- if문에 사용되는 매개변수가 같은 패턴을 보일 때는 맵 자료구조가 유용함.

 

 

- if 문에 (path in urlMap)을 두고 urlMap에 path가 있는지 확인한다.- urlMap[path](req, res); 와 같이 urlMap에 path값으로 매핑된 함수를 실행한다.- 34번 라인에는 urlMap을 정의해준다. 라우터 규칙 매핑 키로 path가 들어가고 값에 함수를 할당한다.- 주의할점은 user함수나 feed 함수들이 정의된 뒤에 urlMap을 정의해 줘야한다. (const로 선언한 변수들은 초기화 전에 읽을 수 없다고함)

 

+) 호이스팅(hoisting) = 함수, 클래스, 변수를 끌어올려서 선언되기 전에 사용하도록 하는 기능. JavaScript에서는 var로 선언한 것들이 호이스팅이 됨. (즉, 어디서 선언이 되든지 참조 가능)- let, const, 함수 표현식, 클래스 표현식은 호이스팅 되지 않음.

 

 

 

 

 

 

▶ 익스프레스 프레임워크 

- 현재까지 Node.js의 기본 라이브러리로 웹 서버를 만들어왔는데, 서비스 가능한 서버이지만 기능이 많이 부족하다. 

- 실전에서 사용하는 서버라면 라우터에서 HTTP 메서드로 각각 요청을 받는 기능, 템플릿 엔진 기능, 미들웨어 등의 기능을 제공해야한다.

 

 

▷ 웹서버가 일반적으로 제공하는 기능들

⦁ 라우팅 

- URL 요청을 함수와 매핑시켜주는 기능

⦁ 정적 파일 서비스

- CSS, JavaScript, 이미지 등의 정적인 파일을 다루는 기능

⦁ 템플릿 엔진

- 동적인 웹 페이지를 HTML과 인스턴스를 사용해 생성하는 기능

⦁ 요청(Request) 데이터 다루기

- HTTP 요청을 추상화하여 편리하게 다룰 수 있게 하는 기능

⦁ 응답(Response) 데이터 다루기

- HTTP 응답을 커스터마이징 할 수 있는 기능. 파일 내려받기, 이미지 출력 등

⦁ 파일 업로드

- HTTP로 전송된 파일을 읽고 다룰 수 있는 기능

⦁ 쿠키 및 세션 지원

- 클라이언트 측 혹은 서버 측의 메모리에 일정 기간 동안 저장해야 하는 데이터를 다루는 기능

⦁ 리다이렉트

- 서버의 응답시 다른 페이지로 전달(redirect)시키는 기능

⦁ 에러 페이지

- 요청이 잘못되었거나, 서버 에러시 특정 에러 페이지를 보여주는 기능

⦁ 미들웨어

- 요청 혹은 응답 사이에 공통된 기능을 추가하는 기능

 

Node.js에는 위와 같은 기능들을 제공하는 오픈 소스 웹 서버가 많으며 그중 익스프레스가 가장 많이 사용된다고 함.

 

 

▷ 익스프레스 설치

 

적당한 폴더 위치를 만들어서 express를 설치한다. 

npm install express 명령 사용(npm = nodejs package manager)

 

 

 

express 패키지를 로딩해 express에 할당한다.

express() 함수 실행으로 express 인스턴스를 만들고 app에 할당한다.

app.get을 사용해 url의 path가 '/'이면서 http 메서드가 get()인 경우 콜백 함수를 실행한다. (인코딩 설정 후 "Hello Express" 출력)

listen 함수를 사용해 클라이언트의 요청을 기다리고, 위에서 정의한 3000 포트를 사용한다.

 

 

(익스프레스가 위치한 곳에 파일을 만들어야 동작하는 듯하다.)

 

 

Node.js 라이브러리로 만든 서버를 익스프레스로 구현하기

- 위에서 만든 라우터 코드를 Express로 리펙터링 해보겠다.

 

 

- Node.js 라이브러리 코드에서의 urlMap으로 url 매핑을 관리하던 부분을 없애고, app.get() 함수에 등록하도록 하였다.

(url 매핑이 많아질 경우 이전보다 편리해지고 깔끔하게 유지할 수 있음)

- res.end() 함수로 화면에 비춰주는 것 대신, res.json() 함수를 사용하여 응답을 JSON 타입으로 보여주며 charset을 utf-8로 자동 설정해주는 기능이 있다.

- const로 선언되어 있던 기능들을 function을 사용하도록 변경하였다. (호이스팅을 사용하기 위함)

- feed의 parameter로 "_" 사용하였다. 이는 사용하지 않는다는 표시이며 사용하지 않는 변수는 빼는 것이 원칙이지만, 함수 인터페이스 구조상 넣을 수 밖에 없을 때 사용하는 관례이다.

 

 

▷ 간단한 게시판 만들기

- 익스프레스로 간단한 게시판 API를 만들어 보겠다.

- REST API 원칙을 따른 메모리 기반으로 동작하는 휘발성 게시판을 만든다. (DB x)

↳ REST API = 자원을 URL에 표현하고 자원을 가져오는 행위를 HTTP 메서드로 표현하는 규칙

 

경로 HTTP 메서드 설명
/ get 게시판 목록을 불러옴
/posts post 게시판에 글 작성
글은 id, 제목, 작성자, 내용, 생성일자로 구성
/posts/:id delete 게시글 아이디가 id인 글을 삭제

 

 

- express 모듈 받아오고 게시글 리스트로 사용할 posts 배열을 둔다.

- req.body를 사용하기 위해서는 JSON 미들웨어를 사용해야 하는데(사용하지 않으면 undefined 반환됨) app.use() 함수에 express.json()을 넣어 JSON 미들웨어를 활성화 시킨다.

- url 인코딩 설정을 해준다. (body에 Key=Value 조합 형태의 데이터 타입을 위한 설정)

- app.get("/")에는 "/"로 오는 요청에 실행된다. res.json()을 통해 리스트를 JSON 형식으로 보여준다.

- app.post("/posts")는 "/posts"로 요청이 오면 실행되고, HTTP 요청의 body 데이터를 변수에 할당한다. (변수 생김새가 근본없게 생겼다)

- posts의 push() 함수를 통해 새로운 게시글 정보를 리스트에 담아주고 JSON 형식으로 보여준다.

- app.delete("/posts/:id")는 데이터 삭제를 위해 사용할 것이다. 라우팅 규칙에 :id로 표기된 부분은 해당 부분에 데이터가 들어오면 문자열 타입으로 params.id에 할당된다. (스프링 RequestMapping 할 때, "/{id}"와 같다고 보면됨)

const filteredPosts = posts.filter((post) => post.id !== +id);

↳ 이 거지같이 생긴 코드는 글 삭제 로직이다. 게시판의 글에서 id 이외의 글들만 뽑아서 filteredPosts에 재할당한다. 

(모던 자바스크립트에서 주로 filter() 함수를 사용하여 배열 요소를 삭제하는 편이라고 함. +id는 문자열인 id를 숫자형으로 변경한다는 뜻이라함😂😂)

- isLengthChanged는 기존 게시판과 필터링된 게시판 글의 길이를 비교하여 게시글 삭제를 판별한다.

- posts에 필터링된 리스트를 담고 판별 결과에 따라 응답을 띄워준다.

 

 

그럼 실행시켜 보겠다.

 

Node.js를 실행시키고 "curl -X GET http://localhost:3000"을 통해 curl로 get 호출한다.

 

curl -X POST ... 로 값을 넣고, curl localhost:3000으로 값을 확인한다.

 

 

로컬에서 띄워도 잘 나오는것을 볼 수있다.

 

 

게시글 지우기 명령이다.

id 1번을 처음 지우면 OK 응답과 delete를 수행한다. 두번째는 수행하지 않는다.

 

delete 결과

 

 

지금까지 REST API 게시판을 만들어 보았다.

curl 옵션 몇가지를 정리하고 글 마무리하겠다.

 

옵션 사용 예시 설명
-X -X POST HTTP 메서드 정보
-d -d "key1=value1&key2=value2" localhost:3000 POST 통신시 body 데이터
-H Content-Type: application/x-www-form-urlencoded 헤더 정보
-x curl -x http://proxy_server:proxy_port --proxy-user username:password 프록시 서버 설정
-T curl -T file.txt http://server.com 파일을 서버에 전송시 사용
-A curl -A "Mozilla/5.0" http://server.com 유저 에이전트를 변경
-i curl -i https://suldenlion.tistory.com/ 서버의 응답을 결과로 출력
-I curl -I https://suldenlion.tistory.com/ 서버 응답에서 헤더 값만 출력
-O curl -O http://server.com/test.txt 서버의 파일을 이름 변경없이 내려받기
-L curl -L http://server.com/redirectingURL 리다이렉트 URL 따라가기
-s curl -s localhost:3000 에러가 발생해도 출력 x (silent)
-S curl -S localhost:3000 에러 발생시 에러 출력 o (Show)

 

반응형

댓글