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