ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • NodeJS #4 axios, cookie
    학원/NodeJS 2022. 4. 21. 15:43

     

    axios를 이용한 간단한 페이지 만들기

     

    -> input으로 입력 받은 값을 순서대로 출력하기

     

    **서버 쪽 내용(05_Server05.js)을 바꿨다면 서버를 껐다 켜줄 것

     

     

     

    05_Front.html

    <!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>Node.js Web Server</title>
        <style type="text/css">
            a{color:blue; text-decoration: none;}
        </style>
    
    </head>
    <body>
        <nav>
            <a href="/"> HOME </a>
            <a href="/about"> ABOUT </a>
        </nav>
        <div style="margin-top: 30px;">
            <form id="form">
                <input type="text" id="username">
                <button type="submit"> 등록 </button>
            </form>
        </div>
        <div id="list">
    
    
        </div>
        <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
        <script type="text/javascript">
            getUsers()
    
            document.getElementById('form').addEventListener('submit', async(e)=>{ 
                //e에는 이벤트의 주체(form)가 전달됨
                e.preventDefault();
                const name = e.target.username.value
                console.log(name)
    
                if(!name) {
                    return alert('이름을 입력하세요')
                }
                try{
                    console.log('try')
                    await axios.post('/user', {name})
                    console.log('await')
                    getUsers()
                } catch(err) {
                    console.log('에러')
                }
                e.target.username.value = ''
                console.log('function end')
                //input id="username" 을 빈칸으로 초기화
                //target = form id="form"
            })
    
            async function getUsers(){
                try{
                    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값 마다 익명함수 실행
                        const userDiv = document.createElement('div')
                        const span = document.createElement('span')
                        span.textContent = users[key] //span안에 users의 key값 삽입
    
                        //수정버튼 생성
                        const edit = document.createElement('button')
                        edit.textContent = '수정'
                        edit.addEventListener('click', async () => {
                            const name = prompt('바꿀 이름을 입력하세요')
                            if(!name){
                                return alert('이름을 입력해주세요')
                            }
                            try {
                                await axios.put('/user/' + key, {name})
                                //url = /user/바뀐이름
                                getUsers()
                            } catch(err) {
                                console.error(err)
                            }
                        })
    
                        //삭제 버튼 생성
                        const remove = document.createElement('button')
                        remove.textContent = '삭제'
                        remove.addEventListener('click', async () => {
                            try {
                                let result = confirm('삭제할까요?')
                                if(result){
                                    await axios.delete('/user/' + key)
                                    //url = /user/바뀐이름
                                    getUsers()
                                }
                            } catch(err) {
                                console.error(err)
                            }
                        })
    
                        userDiv.appendChild(span) //div안에 span 삽입
                        userDiv.appendChild(edit) //div안에 수정버튼 삽입
                        userDiv.appendChild(remove) //div안에 삭제버튼 삽입
                        list.appendChild(userDiv) //div를 list에 삽입
                    })
                } catch(err) {
                    console.error(error)
                }
            }
            
        </script>
    </body>
    </html>

     

     

     

    05_Server05.js

     

    const http = require('http')
    const fs = require('fs').promises
    
    let users = {} 
    //서버가 종료될 때까지 값이 유지되는 변수
    
    http.createServer( async (req, res)=>{
        try { 
            if(req.method == 'GET') {
                if(req.url === '/') {
                    //첫번째 페이지로 갈 때
                    const data = await fs.readFile('./05_Front.html')
                    res.writeHead(200, {'Content-Type' : 'text/html; charset=utf-8'})
                    return res.end(data)
                    //파일 내용 전송 후 async(req, res)=>{} 함수가 
                    //종료되도록 return
                    //return이 없으면 try문 아래에 400 NOTFOUND도 같이 실행되게 됨
                } 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'){
                    return res.end(JSON.stringify(users))
                    //user 객체 안의 내용을 json 형식으로 변경하여 전송
                }
            } else if (req.method == 'POST'){
                if(req.url === '/user') {
                    //req에 전송된 자료를 stream 형식으로 받아 body변수에 대입
                    let body = ''
                    req.on('data', (data)=>{
                        body += data
                    })
                    //req.on() : request의 동작을 첫 번째 인수로 전달된 키워드로 구분, 익명함수를 실행한다. 
                    //전달된 자료를 모두 객체 형식으로 받아서 처리한다. 
                    return req.on('end', ()=> {
                        const {name} = JSON.parse(body)
                        //전달된 데이터를 name 변수에 저장
                        const id = Date.now() //id 변수에 날짜를 추출
                        users[id] = name;
                        res.writeHead(201, {'Content-Type' : 'text/plain; charset=utf-8'})
                        res.end('ok') //함수에서 빠져 나감
                    })
                } 
            } else if (req.method == 'PUT') {
                if(req.url.startsWith('/user/')){
                    // axios.put('/user/' + key, {name}) 이 전달되기 때문에 
                    // '/user/'로 시작하는 주소를 찾도록 함
                    
                    const key = req.url.split('/')[2] 
                    //  req.url.split('/')[0]: , [1]: user, [2]: key
                    console.log(key)
                    // 1650515427987 (POST ('/user')에서 설정한 Date.now()의 값)
    
                    let body = ''
                    req.on('data', (data)=> {
                        body+= data
                    })
    
                    return req.on('end', ()=>{
                        users[key] = JSON.parse(body).name
                        res.writeHead(201, {'Content-Type' : 'text/plain; charset=utf-8'})
                        res.end('ok')
                    })
                
                }
            } else if (req.method == 'DELETE'){
                if(req.url.startsWith('/user/')){
                    const key = req.url.split('/')[2]
                    delete users[key]
                    res.writeHead(201, {'Content-Type' : 'text/plain; charset=utf-8'})
                    return res.end('ok')
                }
            }
    
            //위 if-else 문 어디에도 걸리지 않을 경우 
            //아래 코드를 실행
            res.writeHead(404)
            return res.end('NOT FOUND')
    
        } catch(err) { 
            //서버 실행상의 에러를 처리 
            // : 404 에러 등은 여기서 처리 안됨
            console.error(error)
            res.writeHead(500, {'Content-Type' : 'text/html; charset=utf-8'})
            return res.end(err.message)
        } 
    }).listen(8090, ()=>{
        console.log('Server is ready in port 8090')
    })

     

     

    결과

     

     

     

     

     


     

     

    Cookie Server

     

    쿠키 Cookie 

     

    requesst의 단점 : 누가(어떤 client가) 보낸 요청인지 알수 없다. (ip주소와 브라우저 정보 정도만 알 수 있음)

    -> 쿠키로 이 단점을 해결가능

    쿠키는 키:값의 쌍으로 이루어진 데이터로 매 요청(request)마다 서버에 쿠키가 동봉되어 보내진다

    -> 이 쿠키를 읽어 어떤 client로 부터의 요청인지 파악이 가능함

     

    http 요청과 응답은 헤더와 본문을 가지는데 헤더는 요청/응답의 내용을 담고 있고 본문은 주고 받는 실제 데이터를 가지고 있다.

    -> 쿠키는 부가적인 정보이므로 헤더에 저장하는 것

     

    쿠키를 직접 넣어 구현하려면 writeHead 메서드를 이용해 요청 헤더에 입력한다 

    쿠키의 내용은 Set-Cookie로 브라우저에 쿠키를 설정하라고 명령을 내린다 

     

     

    const http = require('http')
    http.createServer((req, res) => {
        console.log(req.url, req.headers.cookie)
        //클라이언트 요청에는 header의 쿠키가 자동으로 동봉됨
        res.writeHead(200, {
            'Set-Cookie' : 'mycookie=test'
        })
        res.end('<h1>Hello Cookie</h1>')
    }).listen(8090, () => {
        console.log('Server is ready in port 8090')
    })

     

     

     

    쿠키 이용하기

     

     

    07_CookieServer02.js

    const http = require('http')
    const fs = require('fs').promises
    const url = require('url')
    const qs = require('querystring')
    
    const parseCookies = (cookie='') => 
    cookie  
        .split(';')
        .map(v=>v.split('='))
    
    //cookie1=test1; cookie2=test2; 형태로 전달되는 쿠키를 
    //쿠키 단위로 나누고 다시 '='로 분리
        .reduce(( acc, [k, v]) =>{
            acc[k.trim()] = decodeURIComponent(v)
            return acc
        }, {})
    // 분리된 cookie1, test를 각각 k와 v에 전달 -> 객체 형태로 만든 후 acc에 저장 --> 마지막 {} 는 분리된 쿠키들이 [k, v] 형태가 되어 객체로 저장 취합된다는 의미 
    
    
    http.createServer(async (req, res) => {
        
        const cookies = parseCookies ( req.headers.cookie )
        // key:value 형태로 변환후 cookies 변수에 저장 
    
        if(req.url.startsWith('/login')) {
            //쿼리 스트링 분리
            const {query} = url.parse(req.url)
            console.log(query) //name=%EA%B9%80%EC%A0%9C%EB%8F%99
            const {name} = qs.parse(query)
            
            const expires = new Date()
            //쿠키 유효시간 설정을 위해 현재 날짜 데이터 생성
            expires.setMinutes(expires.getMinutes()+1) 
            //쿠키 유효시간을 현재 시간 +1분으로 설정 
    
            res.writeHead(302, {
                Location : '/',
                'Set-Cookie': `name=${encodeURIComponent(name)}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`
            })  //경로및위치, 유효시간, 쿠키 접근을 http 방식으로만 제한
    
            res.end()
    
        } else if(cookies.name) { //로그인 되어 세션값이 존재하는 경우
    
            res.writeHead(200, {'Content-Type' : 'text/plain; charset=utf-8'})
            res.end(`${cookies.name}님 안녕하세요`)
                
            }else {
                try {
                    const data = await fs.readFile('./06_Cookie_page.html')
                    res.end(data)
                } catch(err) {
                    res.writeHead(500, {'Content-Type' : 'text/plain; charset=utf-8'})
                    res.end(err.message)
                }
            }
        }
    ).listen(8090, () => {
        console.log('Server is ready in port 8090')
    })

     

     

    06_Cookie_page.html

    <!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>Document</title>
    </head>
    <body>
    <form action="/login">
        <input id="name" name="name" placeholder="please enter your name">
        <button id="login">login</button>
    </form>
    </body>
    </html>

     

     

    검사 창에서 쿠키의 정보 확인 가능

     

    -> 쿠키의 유효시간을 1분으로 설정했기 때문에 '~님 안녕하세요' 화면에서 1분 후 새로 고침하면 처음 로그인 창으로 돌아간다 

     

     

     

    session에 cookie 값을 저장한 후 이용하기

     

    const http = require('http')
    const fs = require('fs').promises
    const url = require('url')
    const qs = require('querystring')
    
    const parseCookies = (cookie='') =>
        cookie 
            .split(';')
            .map(v=>v.split('='))
            .reduce(( acc, [k, v]) =>{
                acc[k.trim()] = decodeURIComponent(v)
                return acc
            }, {})
    
    const session = {}
    
    http.createServer(async(req, res) => {
        const cookies = parseCookies(req.headers.cookie) 
        
        if(req.url.startsWith('/login')) {
            const {query} = url.parse(req.url)
            const {name} = qs.parse(query)
            
            console.log('name : ' + name)
    
            //세션, 쿠키 수명 계산
            const expires = new Date()
            expires.setMinutes(expires.getMinutes() + 1)
    
            //세션 객체에 저장하기 위한 고유 키값
            const uniqueInt = Date.now() 
    
            //Cookies -> name = ${uniqueInt}
            //Session -> ${uniqueInt} : '홍길동'
            //의 형태로 저장됨
            //세션의 값들은 서버에서 관리 : 세션에서 해당값의 유무, 쿠키 값의 유무를 검사해 조회한다
    
            session[uniqueInt] = {
                name, 
                expires,
            }
    
            res.writeHead(302, {
                Location: '/',
                'Set-Cookie' : `session=${uniqueInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`
            })  
            //쿠키에는 고유 키 값(uniqueInt)을 value로써 session key에 저장 (실제 값은 없음)
            res.end()
        } else if(cookies.session && session[cookies.session].expires > new Date() ) { 
            //쿠키에 session이라는 key가 존재 && session의 유효시간이 지나지 않있을 때
    
            console.log(cookies.session)
            res.writeHead(200, {'Content-Type' : 'text/plain; charset=utf-8'})
            res.end(`${session[cookies.session].name}님 안녕하세요`)
    
        } else {
            try {
                const data = await fs.readFile('./06_Cookie_page.html')
                res.end(data)
            } catch(err) {
                res.writeHead(500, {'Content-Type' : 'text/plain; charset=utf-8'})
                res.end(err.message)
            }
        }
    }).listen(8090, () => {
        console.log('Server is ready in port 8090')
    })

     

    결과는 위에 cookie 이용하기 항목과 같으나 

    cookie에는 고유 키값만 저장하고 session에 실제 data를 저장해 불러온다는 방식이 다르다.

     

    댓글

Designed by Tistory.