Node.JS)04.21(HttpServer,  Axios )
Programming/JS

Node.JS)04.21(HttpServer, Axios )

728x90

목차

 


 

1. HttpServer

  • Node.js에 포함된 기능과 문법을 이용해서 웹호스팅을 할 수 있는 서버를 구축합니다.

1.1. http모듈을 require

  • 서버 구축에 필요한 기능과 함수를 담고있는 'http모듈을 require'한다.
// 서버 구축에 필요한 기능과 함수를 담고있는 'http모듈을 require'한다.
const http = require('http');

1.2. createServer( )

  • Node.js로 만든 http서버를 실행하는 함수.
  • (req, res)=>{ } : 서버로 클라이언트의 요청이 있을 때 실행할 명령들이 들어간다.
  • req, res를 전달받은 익명함수가 클라이언트로부터 들어온 요청에 응답한다.
http.createServer( (req, res) => {
    // req는 요청을 받고, res는 응답을 한다.
    res.write('<h1>Hello Node Server!!</h1>');
    res.write('<h2>Welcome to my Node Server!!</h2>');

} )
.listen(8090, ()=>{ console.log('8090포트에서 서버가 대기중입니다.');  } );
  • .listen(8090, ( ) => { } ) 에 있는 ' ( )=>{ } ' 는 서버가 시작되면 실행할 명령이 들어간다.
  • 8090 : 클라이언트가 요청할 포트번호

 


1.3. http서버 - 에러처리

  • createServer() 함수로 서버기능을 실행시킨다. + 에러처리 구문도 추가한다.
  • createServer() 함수로 만든 '서버 객체를 server 변수에 저장'하고
    기타설정은 server변수를 통해 별도로 실행한다.
  • server변수.on( ) 메서드로 서버에 관련한 여러가지 설정이 가능하다.
const http = require('http');

// create 서버함수로 서버기능을 실행시킨다. + 에러처리 구문도 추가한다.
// createServer 함수로 만든 서버 객체를 server 변수에 저장하고 기타설정은 server변수를 통해 별도로 실행한다.
const server = http.createServer((req, res)=>{
    // 서버 요청시 응답내용이 쓰여진다.
    res.write('<h1>Hello Node Server!!</h1>');
    res.write('<h2>Welcome to my Second Server!!</h2>');
    res.write('<h3>Welcome to my Node Server!!</h3>');
});

server.listen(8090, ()=>{
    console.log('8090번 포트에서 서버 대기중입니다.'); 
});    // 포트번호(8090) 설정 

// server.on('listening', ()=>{
//     console.log('8090번 포트에서 서버 대기중입니다.'); 
// });  // 기타설정1 : 서버 실행시 동작

server.on('error', (error)=>{
    console.error(error);
}) ;    // 기타설정2 : 에러처리


1.4. 한 번에 여러 개의 서버를 실행

  • createServer( )를 여러번 호출하는 방식
  • 단, 두 서버의 포트번호를 다르게 지정해야 한다. (포트번호가 충돌하면 에러가 발생)

 

 
const http = require('http');

// 첫 번째 서버(8081 포트)
http.createServer((req, res)=>{
    //응답 내용의 본문 전송
    res.write('<h1>Hello Node Server #1</h1>');
    //응답 내용의 마지막 전송 : res.end 실행 후에는 더 이상 응답내용이 전송될 수 없다.
    res.end('<p>Hello Server!</p>');
}).listen(8081,()=>{   // 포트번호와 함께 이벤트 리스너 설정
    console.log('8081번 포트에서 서버 대기중입니다!');
});


// 두 번째 서버(8082 포트)
http.createServer((req, res)=>{
    res.write('<h1>Hello Node Server #2</h1>');
    res.end('<p>Hello Server!</p>');
}).listen(8082,()=>{
    console.log('8082번 포트에서 서버 대기중입니다!');
});

 

1.4.1. req, res

  • request, response의 의미를 갖는 변수이다.
  • 매개 변수이고, 서버에 있는 실제 request, response 객체가 전달된다.
  • 매개 변수는 그 객체를 전달받아 사용하는 것으로 변수의 이름은 자유롭게 변경이 가능.
    다만, 함수 내에서 변경된 이름을 일관되게 사용해주는 것이 중요하다.

 


1.5. http서버 - html 파일 표시  

fs모듈을 활용해 html파일을 불러와 변수에 담고, res.end(변수)로 페이지에 표시한다! 

const http=require('http');
const fs = require('fs').promises;

http.createServer(async (req, res)=>{
    try{
        const data = await fs.readFile('./04_Server.html');
        // 헤더에 필요한 내용을 실어서 보내는 역할. 현재는 한글을 위한 속성이 담겨있다.
        // 따라서 한글 인코딩을 위한 헤더 내용을 먼저 보내준다.
        res.writeHead(200,{'Content-type' : 'text/html; charset=utf-8'});
        res.end(data);
        // write(일반전송), writeHead(헤더 내용 전송), end(전송 후 종료를 위한 함수)
    }catch(err){
        console.error(err);
        res.writeHead(500,{'Content-type' : 'text/plain; charset=utf-8'});
        res.end(err.message);
    }
}).listen(8081, ()=>{
    console.log('8081번 포트에서 서버 대기중입니다.');
} );
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>04_Server.html</title>
</head>
<body>
    <h1>Node.js 웹서버 </h1>
    <h2>만들 준비되셨나요?</h2>
</body>
</html>

 

1.5.1. writeHead()에 담는 html상태코드의 종류

  • 2XX : 서버 전송 정상 완료.
  • 3XX : 리다이렉션(다른페이지로 이동)을 알리는 상태
  • 4XX : 요청 오류를 나타낸다.  요청 자체에 오류가 있을 때 표시된다.
  • 5XX : 서버오류 - 요청은 제대로 왔지만 서버에 오류가 생겼을 때 발생한다.

 


1.6. http서버 - url 매핑

if( req.url === ' / '){ } 식으로 매핑하게 된다.

const http = require('http');
const fs = require('fs').promises;

let users = {};     // 입력폼에서 등록된 이름들이 담길 객체

http.createServer( async (req, res)=>{
    try{
        
        if(req.method=='GET'){      //조회(SELECT)용도로 쓰임

            if(req.url === '/'){
                //fs.readFile('./05_Front.html').then((data)=>{});
                const data = await fs.readFile('./05_Front.html');
                res.writeHead(200, {'Content-type':'text/html; charset=utf-8'});
                return res.end(data);   // return은 현재 위치에서 익명함수를 종료하는 것으로 이해하는 것이 편함

            }else if(req.url==='/about'){   
                const data = await fs.readFile('./05_about.html');
                res.writeHead(200, {'Content-type':'text/html; charset=utf-8'});
                return res.end(data);

            }else if(req.url ==='/users'){
                // json 데이터 전송을 위한 헤더 설정
                res.writeHead(200, {'Content-type':'application/json; charset=utf-8'});
                //user 객체 안의 내용을 json 형식으로 변경하여 전송
                return res.end( JSON.stringify(users) );
            }

        }else if(req.method=='POST'){   //로그인 또는 INSERT 용도로 쓰임

            if(req.url === '/user'){
                // req에 전송된 자료(name)을 Stream형식으로 받아 body변수에 넣는다.
                let body = '';
                req.on('data', (data)=>{    // {'name' : '홍길동'}
                    console.log('data : ', data.toString());
                    body+=data;
                    console.log('body : ', body);
                });
                // req.on(); : request의 동작을 첫 번째 인수로 전달된 키워드로 구분하여, 같이 전달된 익명함수를 실행한다. 
                // 'data' : 함께 전달된 자료 수신 및 처리
                // 전달된 자료가 두 개 이상이여도 모두객체 형식으로 다 받아 처리한다.

                return req.on('end', ()=>{
                    const {name} = JSON.parse(body);    // 전달된 데이터의 값을 꺼내서 name 변수에 저장
                    const id = Date.now();  // id변수에 날짜를 추출(날짜 현재시간을 밀리초로 얻어낸 값)
                    users[id] = name;   // 키값은 id, 밸류는 name으로 객체에 저장
                    res.writeHead(201, {'Content-Type':'text/plain; charset=utf-8'});
                    res.end('ok');   //원래 자리로 복귀
                });
                // 마지막 전송과 끝내기 위한 리턴.
                // 단순히 req.end()만 실행되는 것이 아닌, 다른 동작이 함께 실행되어야 한다면 아래와 같이 익명함수를 'end'키워드와 함께 실행. 
                // 그 여러 실행들이 실행되고 리턴 & 종료된다.
            }
        }else if(req.method=='PUT'){    //특정 자료를 수정(UPDATE)할 때

            // 요청내용 : axios.put('/user/'+key, {name});
            // console.log(req.url); /user/151232355243
            if(req.url.startsWith('/user/')){
                const key = req.url.split('/')[2];
                let body = '';
                //data <- {name:실제전송된값}
                req.on('data', (data)=>{
                    body += data;
                });
                return req.on('end', ()=>{
                    users[key] = JSON.parse(body).name;
                    res.writeHead(200, {'Content-Type':'text/plain; charset=utf-8'});
                    return res.end('수정 ok'); 
                });
            }

        }else if(req.method == 'DELETE'){ //DELETE 용도로 사용

            if(req.url.startsWith('/user/')){
                const key = req.url.split('/')[2];
                delete users[key];
                res.writeHead(200, {'Content-Type':'text/plain; charset=utf-8'});
                return res.end('삭제 ok');
            }
        }
        
        res.writeHead(404);
        return res.end('NOT FOUND');

    }catch(err){
        console.error(err);
        res.writeHead(500, {'Content-type':'text/plain; charset=utf-8'});
        res.end(err.message);
    }
} ).listen(8090, ()=>{ console.log('8090포트에서 서버가 대기중입니다.'); });

 

1.6.1. req.method의 종류

  • req.method == 'GET' : 조회(SELECT)용도로 쓰임
  • req.method == 'POST' : 로그인 또는 INSERT 용도로 쓰임
  • req.method == 'PUT' : 특정 자료를 수정(UPDATE)할 때
  • req.method == 'DELETE' : DELETE 용도로 사용
 

1.6.2. axios 라이브러리

  • jquery와 같은 종류의 javascript 라이브러리
  • http통신을 하는데 매우 인기있게 사용되는 라이브러리
  • 브라우저와 Node.js 플랫폼에서 모두 사용가능
  • 현재는 브라우저 상에서 사용하기 위해 src로 로딩했으며,
    익스프레스 서버에서 사용하려면 설치명령으로 설치 후 사용한다.
  • json데이터 자동변환 기능이 있고, get, post, put, delete등 다양하게 구분하여 request요청이 가능하다.
  • 일반적으로 form의 'submit이벤트를 이용하면 페이지 전환'이 일어난다.
    ( 전송 - 처리 - 새로운 페이지 로딩 )
    페이지 전환 없이 등록 절차 실행 후 현재 위치로 되돌아오기 위해 'axios 객체를 이용'한다.
    ( 전송 - 처리 - 현재페이지로 복귀 )
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>05_Front.html</title>
    <style type="text/css">
        a{color:blue; text-decoration:none;}
    </style>
    <script src = "https://unpkg.com/axios/dist/axios.min.js"></script>	// axios 라이브러리
</head>
<body>
    <nav>
        <a href="/">Home</a><br>
        <a href="/about">About</a><br>
    </nav>
    <div>
        <form id="form">   
            <input type="text" id="username">
            <button type="submit">등록</button>
        </form> 
    </div>
    <div id="list"></div>
    <!-- 스크립트를 이용한 이벤트 리스너는 head부분에 정의하면 실행에 오류가 많다, 따라서 지금처럼 body부분에 정의한다. -->
   <script type="text/javascript">
       // addEventListener : 클릭 또는 더블클릭 또는 현재의 form태그에서 있을 수 있는 submit과 같은 이벤트가 발생하면 전달인수로 전달된 익명함수를 실행해주는 함수(전달인수 : 이벤트 이름과 익명함수를 함께 전달해준다.) id가 form인 개체에 submit이벤트가 일어나면, (e)=>{} 익명함수가 실행된다. e변수에는 이벤트의 주인공인 form이 전달되어 함수가 실행된다.

       // form이 submit되는 때를 잡아 실행될 이벤트 리스너 설정
       document.getElementById('form').addEventListener('submit', async (event)=>{ 
           // 현재 익명함수에서는 document.getElementById('form')을 부르기 위한 이름이 없으므로, 함수의 매개변수로 event를 만들고 이용한다. 
           // event <- document.getElementById('form')
           event.preventDefault(); //form의 이벤트(submit)가 계속 진행되면 화면전환이 일어나므로, 이벤트동작(submit)을 멈춤
            const name = event.target.username.value;
            //alert(name);

            if( !name ){
                return alert('이름을 입력하세요');
            }
            try{
                await axios.post('/user', {name});
                // axios의 특성상 서버에서 보내오는 응답은 현재 위치로 응답되어져 복귀한다.
                //const data = await axios.post('/user', {name});  <- 반환되는 값이 있을 경우 처리방법

                //복귀 후 해야할 일 : 현재 등록된 user들을 조회해서 화면에 표시한다.
                // 이름이 보여질 곳 : <div id="list"></div>
                getUsers();
            }catch(err){
                console.error(error);
            }
            event.target.username.value = '';
        });

        async function getUsers(){
            try{
                // get 메서드 방식의 /users 요청 , 결과형식 json형식
                const res = await axios.get('/users');

                const users = res.data; // 요청에 대한 반환(리턴) 값을 객체 형식으로 변환 (키 : 값)
                const list = document.getElementById('list');
                list.innerHTML = '';

                //users 변수에 있는 키값들을 전달인수로 하여 키값 개수만큼 반복실행
                Object.keys(users).map( function(key){
                    // users에서 key값들을 추출 -> 각 key별로 function(key){}를 실행
                    // 추출된 키들 중 하나에 대해 실행될 함수. 
                    // map에 의해 키 개수만큼 실행
                    const userDiv = document.createElement('div');  // div태그 생성
                    const span = document.createElement('span');    // span태그 생성
                    span.textContent = users[key];                  // span태그 안에 키값으로 얻어낸 users값을 삽입

                    // 수정버튼 생성
                    const edit = document.createElement('button');  //<button></button>
                    edit.textContent = '수정';  // 버튼에 쓰여질 라벨
                    edit.addEventListener('click', async ()=>{
                        const name = prompt('바꿀 이름을 입력하세요');  //수정할 이름 입력
                        if(!name){
                            return alert('이름을 반드시 입력해야 합니다.')
                        }
                        try{
                            await axios.put('/user/' + key, {name});
                            getUsers();
                        }catch(err){
                            console.error(err);
                        }
                    });
                
                    // 삭제버튼 생성
                    const remove = document.createElement('button');
                    remove.textContent = '삭제';
                    remove.addEventListener('click', async ()=>{
                        try{
                            await axios.delete('/user/'+key);
                            getUsers();
                        }catch(err){
                            console.error(err);
                        }
                    });

                    userDiv.appendChild(span);		// div안에 span 삽입
                    userDiv.appendChild(edit);		// div안에 edit버튼 삽입
                    userDiv.appendChild(remove);	// div안에 remove버튼 삽입
                    list.appendChild(userDiv);		// div태그를 list 태그에 삽입
                }  );

            }catch(err){
            }
        }
        
        window.onload = getUsers;
    </script>
</body>
</html>

 

addEventListener() :

  • 클릭 또는 더블클릭 또는 현재의 form태그에서 있을 수 있는 submit과 같은 '이벤트'가 발생하면 전달인수로 전달된 익명함수를 실행해주는 함수( 전달인수 : 이벤트 이름과 익명함수를 함께 전달해준다. )
  • id가 form인 개체에 submit이벤트가 일어나면, ( e )=>{ } 익명함수가 실행된다.
    'e변수'에는 이벤트의 주인공인 'form이 전달'되어 함수가 실행된다.

 

preventDefault() : 
  • form의 이벤트(submit)가 계속 진행되면 화면전환이 일어나므로, 이벤트동작(submit)을 멈춤

 

 

300x250