지식이 늘었다/웹개발

NodeJS (2) NPM, ExpressJS 튜토리얼

PurpleGuy101 2024. 11. 30. 12:05

 

const http = require('http');

function handleRequest(request, response){
	response.statusCode = 200;
    response.end('<h1>Hello World!</h1>');
}

const server = http.createServer(handleRequest);

Server.listen(3000);

 

이제 NodeJS를 더 자세히 알아보자.

지금 하고 싶은 건, URL의 세부경로에 따라서 다른 응답을 생성하는 기능을 만들어 보는 것이다.

 

function handleRequest(request, response) {

	// localhost:3000/currenttime

	// localhost:3000
    response.statusCode = 200;
    response.end('<h1>Hello World!</h1>');

}

 

가령 localhost:3000/currenttime의 URL로 Request가 요청되면,

해당 datetime을 Response로 반환하고,

그게 아니면 hello world!를 반환하도록 만들어 보자.

 

function handleRequest(request, response) {

// localhost:3000/currenttime
    if (request.url === '/currenttime'){
        response.statusCode = 200;
        response.end('<h1>'+ new Date().toISOString() + '</h1>');

    }

// localhost:3000
    else if (request.url === '/') {
        response.statusCode = 200;
        response.end('<h1>Hello World!</h1>');
    }
}

 

new Date()를 통해서 현재 시간을 나타내는 객체를 받고

toISOString()메소드를 통해 스트링으로 바꿔주자.

 

위의 코드를 터미널상에서 실행하면

 

URL에 따라서 다른 결과를 출력하는 것을 확인할 수 있다.

 

---

 

지금까지 NodeJS의 기초를 통해서

서버에서 실행되는 코드를 작성하는데 필요한 기초를 배웠다.

하지만 노드JS만으로는 한계가 있다.

 

모든 웹사이트에 대해서 해당 handling function을 하나하나 새로 작성해줘야 하는데,

프로젝트가 복잡해질수록 이러한 low level코드만으로는 개발하기 복잡하고 귀찮아지는것을 느끼게 될 것이다.

온라인 쇼핑몰처럼 많은 다른 페이지에 대해서 유저데이터를 파싱하는 경우와 같이 말이다.

 

그래서 서드파티 패키지를 들고와서 백엔드 개발에 편리함을 더해주도록 하자.

ExpressJS가 노드개발진영에서 가장 크고 편리한 프레임워크이다.

 

ExpressJS를 통해서

Form형태로 제출된 유저Input값을 입력받아서 서버단의 파일에 저장하고

동적인 HTML 페이지를 만드는 방법을 배워보자.

 

---

 

파이썬의 import나 C/C++의 include처럼

타사의 패키지를 import하는 방법을 알아보도록 하자.

 

NodeJS에는 타사 패키지를 import하는 빌트인 기능이 따로 있다.

NPM이라는 기능인데, 시스템에 npm을 설치하자.

Node Pakage Manager인데, 파이썬의 pip같은 외부 패키지 관리도구이다.

 

Node 터미널에서 

프롬프트 창에 npm init 을 입력하자.

대부분의 설치옵션 선택에서 디폴트 값을 선택해서 설치하면 된다.

 

pakage name의 디폴트 이름 -> 그냥 엔터

프로젝트버전 -> 그냥 엔터

패키지 description -> 없어도됨 -> 그냥 엔터

entry point -> 중요하진 않은데 여기서는 app.js로 설정 -> /app.js

git repository -> 무시해도됨. 디폴트값사용 -> 그냥 엔터

extra keyword -> 그냥 엔터

author -> 내이름 입력

license -> 중요하진 않은데 -> MIT입력

 

하고 설치하면 됨

이러면 해당 폴더에 pakage.json이 만들어지는데

이 프로젝트에 대한 configuration(설정)을 나타낸다.

사용한 외부 패키지의 버젼과 dependency(의존성)등을 관리한다.

 

(npmjs.com)에서 expressjs 패키지에 대한 공식문서와 튜토리얼을 배울 수 있다.

 

---

 

(사용할 코드)

// pakage
const express = require('express');
const fs = require('fs'); // filesystem pakage
const path = require('path'); // path pakage
const app = express();

//
app.use(express.urlencoded({extended: false}));
app.get('/currenttime', function(req, res){
    res.send('<h1>'+ new Date().toISOString() + '</h1>')

}); // localhost:3000/currenttime

app.get('/', function(req,res){
    res.send('<form action="/store-user" method="POST"><lable>Your Name</label><input tpye="text" name="username"><button>Submit</button></form>')
}); // localhost:3000/

app.post('/store-user', function(req, res){
    const userName = req.body.username;
    
    const filePath = path.join(__dirname, 'data','users.json' );
    const fileData = fs.readFileSync(filePath);
    const existingUsers = JSON.parse(fileData);
    existingUsers.push(userName);
    fs.writeFileSync(filePath, JSON.stringify(existingUsers));
    console.log(userName);
    res.send('<h1>Username stored!</h1>');
});

app.listen(3000);

 

---

 

이제 expressJS를 사용하기 위해서

기존의 NodeJS로 작성된 코드를 ExpressJS로 나타내어 보자.

 

const express = require('express')

를 통해 Express를 임포트해온 뒤,

 

const app = express();

를 통해서 express와 관련된 기능이 담긴 객체를 app에 저장해준다.

 

require('express'); // <-> const http = require('http');
const app = express(); // <-> const server = http.createServer();
app.get('/currenttime', function(req, res) { // action } ); // <-> handleRequest(request, response); // localhost:3000/currenttime
app.listen(3000)// <-> server.listen(3000);

 

위와 같은 형태로 get Method를 통해서

기존의 nodeJS에서 if-else문으로 url을 지정해서 응답하는 방식에서 벗어나

get메소드로 응답하는 셈이다.

 

또한 anonimous function을 사용해서 

따로 밑에서 function을 정의하지 않고 파라미터 밸류에서 바로 정의해줄 수 있다.

 

(* 람다식으로 표현한다면 아래와 같이 표현될 것이다.)

app.get('/', (req, res) => {
    res.send('<form>...</form>');
});

 

 

앞선 NodeJS코드에서

localhost:3000/currenttime URL로 request가 도착했을 때, dateTime을 반환하는 기능을 구현했었는데,

ExpressJS의 send 메소드를 사용해서 NodeJS의 end메소드 기능을 대체해주자.

 

const express = require('express');
const app = express();

app.get('/currenttime', function(req, res) {
    res.send('<h1>' + new Data().toISOString() + '</h1>');
} );

app.get('/', function(req, res) {
    res.send('<h1>' + 'Hello World!'+ '</h1>');
} );

app.listen(3000)

 

이렇게 해서 아래의 Node JS로 작성했던 코드를 

Express JS로 바꾸어 보았다.

const http = require("http"); // require함수를 통한 http 객체 생성

function handleRequest(request, response) {
  // localhost:3000/currenttime
  if (request.url === "/currenttime") {
    response.statusCode = 200;
    response.end("<h1>" + new Date().toISOString() + "</h1>");
  }

  // localhost:3000
  else if (request.url === "/") {
    response.statusCode = 200;
    response.end("<h1>Hello World!</h1>");
  }
}

const server = http.createServer(handleRequest); // http객체의 createServer 메소드를 통한 서버 객체 생성
server.listen(3000); // 서버 객체의 listen 메소드를 통한 Request 수신

 

긴 코드는 아니지만,

handlerfunction을 정의하고

statusCode와 end 메소드를 경우에 따라서 실행하고 하는 것이 간결해진 것을 알 수 있다.

 

또한, 이는 빙산의 일각일 뿐이며

사용자 입력구문을 파싱하고 처리하는 경우에, Express JS의 진가가 드러나게 될 것이다.

 

---

 

(352. 사용자 입력값 분석)

 

이제 웹서버에 필요한 기능들을 더 추가해보도록하자.

 

const express = require('express');
const app = express();

app.get('/currenttime', function(req, res) {
    res.send('<h1>' + new Date().toISOString() + '</h1>');
} );

app.get('/', function(req, res) {
    res.send('<form></form>');
} );

app.listen(3000)

 

이제 기본경로로 request가 왔을 때, Hello World!말고 다른걸 반환하게 해보자.

 

app.get('/', function(req, res) {
    res.send('<form></form>');
} );

일단은 <form>만 반환하여 브라우저가 알아서 나머지 html 구조를 만들게 하자.

이후에 doctype이나 html파일을 통해서 response를 만들겠지만 말이다.

 

app.get('/', function(req, res) {
    res.send('<form><label>Your Name</label><input type="text"></form>');
} );

이 부분을 수정하여 서버를 만들었다면, 다음과 같은 결과를 얻을 것이다.

 

(서버 quit 단축키 : ctrl+c)

 

---

 

app.get('/', function(req, res) {
    res.send('<form action="/store-user" method="GET"><label>Your Name</label><input type="text" name="username"><button>Submit</button></form>');
} );

 

이후 submit을 위한 버튼을 추가하고

form에 action="/store-user" 와 method="POST"  속성(attribute)를 추가하자.

(attribute는 html 요소에 대한 정적인 속성, property는 자바스크립트 객체(=DOM)에 대한 동적인 속성)

 

---

 

이제 새로운 handler를 추가해주자.

이번에는 post request이다.

 

 

첫번째 파라미터에는 앞선 request에서 post method를 통해 사용자 input을 저장하는 경로인

  '/store-user'를 넣어주고,

두번째 파라미터에는 익명함수를 넣어주자.

 

Express에서 제공하는 객체에서는 

body라는 특수 property를 통해서 request에 첨부된 데이터에 접근할 수 있다.

위의 경우 input 원소에 name="userName"이라는 어트리뷰트가 달아놨기 때문에

req.body.userName으로 바로 접근이 가능하다.

 

app.get('/', function(req, res) {
    res.send('<form action="/store-user" method="POST"><label>Your Name</label><input type="text" name="username"><button>Submit</button></form>');
  }); // localhost:3000/

app.post('/store-user', function(req,res){
    const userName = req.body.username;
    console.log(userName);
    res.send('<h1></h1>');
})

 

위의 코드를 사용해 사용자로부터 받아온 userName을 Node의 콘솔에 출력해보자.

 

입력을 하면...

 

 

위와 같은 에러가 발생하는 것을 알 수 있다.

 

무엇이 문제이냐?

ExpressJS는 주로 Routing Incoming Request(입력되는 라우팅 요청)을 처리하는 라이브러리이다.

그래서 어떠한 요청이 어떠한 경로에서 어떠한 메소드나 함수로 수행되었는지 정의하는 것이

Express를 활용한 주요 작업이다.

 

문제는

req.body에 쉽게 접근은 할 수 있었지만,

이 실제 데이터에 대해서 파싱을 수행하지 않았다점이다.

 

파이썬을 할때에도, map과 같은 특정 데이터를 그대로 print해버리면 안되고

list메소드로 변환해주는 과정이 필요했듯이,

여기서도 req.body를 그대로 console.log로 출력해주려 해버려서 오류가 발생한 것이다.

 

해당오류에 대해 개발자모드에서 Network로 분석을 해보면

빨간색으로 표시된 실패한 요청인 store-user를 볼 수 있다.

 

 

해당 요소의 Header를 보면

Status Code가 500으로 서버내부에서 문제가 발생했음을 알려주고 있다.

그리고 밑에는 브라우저가 자동으로 생성한 여러가지 Header가 있는데,

아래로 쭉 내리거나 옆에 Payload를 보면 FormData가 있다.

 

 

이렇게 Form Data에 

username이 Max는 제대로 보내진 것을 확인할 수 있다.

 

브라우저에 의해 자동적으로 Post Request로 Form을 날렸기 때문에

이러한 Form Data는 제대로 존재하고 있다.

 

중요한건, 이 데이터는 JS코드가 아니라 Raw Text데이터라는 거다.

그래서. 우리는 받은 Request에서 

Form Data를 파싱해줘야한다.

 

그래서 express에서 제공하는 use 메소드를 사용하여 파싱을 해주자.

 express 패키지에서 제공하는 urlencoded라는 미들웨어 함수를 use로 사용하는 기능이다.

app.use(express.urlencoded({extended: false}));

 

 

아래의 use를 추가한 코드로 서버를 동작시킨뒤, Max라는 이름을 submit해보자.

const express = require('express');
const app = express();

app.use(express.urlencoded({extended: false}));



app.get('/currenttime', function(req, res) {
    res.send('<h1>' + new Date().toISOString() + '</h1>');
} );

app.get('/', function(req, res) {
    res.send('<form action="/store-user" method="POST"><label>Your Name</label><input type="text" name="username"><button>Submit</button></form>');
  }); // localhost:3000/

app.post('/store-user', function(req,res){
    const userName = req.body.username;
    console.log(userName);
    res.send('<h1>User name stored!</h1>');
})

app.listen(3000)

 

아까전과는 달리 제대로 /store-user URL로 연결되고

제대로된 문구가 출력되는 것을 확인할 수 있다.

 

 

 

또 이러한 Request에 대해서 Node 콘솔창에서 제대로 username을 파싱하여 출력한 것을 확인할 수 있다.

---

 

출처 : 100일 코딩 챌린지 - Web Development 부트캠프, Maximilian Schwarzmuller, Udempy

https://www.udemy.com/course/100-2022-web-development/