ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Ajax] 중고차 홈페이지 만들기
    Studying/JavaScript 2022. 7. 30. 19:02

    개요

    Node.js 환경에서 서버를 구축하고 Ajax 비동기 통신을 이용하여 중고차 홈페이지를 만듭니다.

     

     

    Ajax : AsynchronousJavaScriptAndXML

    서버와 비동기적으로 데이터를 주고받는 자바스크립트 기술

     

     

    기초 지식

    • 서버란? 클라이언트가 원하는 데이터를 요청하면 주는 곳을 말합니다.
    • 요청 방법
      1. 원하는 데이터 URL을 확인하고
      2. 해당 URL로 GET 요청합니다 (ex) comic.naver.com 으로 요청하면 웹툰 데이터를 보여줍니다.
      3. 또는 주소창에 url을 입력하거나, 버튼 같은 걸 클릭했을 때 GET 요청을 보내곤 합니다
      4. => 하지만 이렇게 하면 웹 브라우저가 새로고침 됩니다.

     

    AJAX, 새로고침 없이 서버에게 GET요청을 하는 JS코드이다.

     

     

    JS로 AJAX 요청하는 세 가지 방법

    1. XHR - 요즘 아무도 안 쓰는 옛날 방식

    var ajax = new XMLHttpRequest();
        ajax.onreadystatechange = function() {
        if(this.readyState == 4 && this.status == 200) {
            console.log(ajax.responseText)
        }
    };
    ajax.open("GET", "http://naver.com", true);
    ajax.send();

    new XMLHttpRequest( ) - 통신 객체

    • 서버와 상호작용하기 위해 사용합니다. 전체 페이지의 새로고침 없이도 url을 통해 데이터를 전송하거나 받을 수 있습니다.
    • XML 말고도 모든 종류의 데이터를 받아올 수 있습니다.
    • 브라우저에서 제공하는 Weba API이기 때문에 브라우저 환경에서만 정상적으로 동작하고 nodejs 환경에서는 제공되지 않는다.

    ajax.open( ) - XMLHttpRequest 객체를 초기화

    • open( method, url, async, user, password) : method는 "GET", "POST", "PUT", "DELETE" 등
      • url에 요청을 처리할 주소 값을 설정합니다.
      • async 비동기 여부를 설정(기본값 true)합니다.

     

    onreadystatechange(이벤트 핸들러) - XMLHttpRequest의 readyState 값이 변경되면 호출 되는 이벤트. readyState 값의 변경에 따라 요청의 처리 결과를 확인할 수 있습니다.

     

    ajax.send( ) - 요청을 서버로 전달합니다. XMLHttpRequest 요청이 비동기인 경우 요청을 보내고 바로 반환, 동기인 경우 응답이 반환될 때까지 대기합니다.

     

     

     

    2. Fetch (XHR의 단점을 보완한 새로운 Web API)

    XML보다 가볍고 JavaScript와 호환되는 JSON을 사용하는 방식입니다.

    fetch('https://naver.com')
    /* 응답 받았을 때의 콜백 함수*/
    .then((response) => {
        return response.json() // .json() : 응답을 json 형태로 전환
    })
    /* 결과를 받았을 때의 콜백 함수 */
    .then((결과) => {
        console.log(결과)
    })
    /* 에러 처리 */
    .catch(() => {
        console.log('에러남')
    })

    fetch( ) - 원하는 데이터 url과 함께 자동으로 GET요청을 보내주는 함수입니다.  이때 url 하나의 인수만 받습니다.

     

    .then(콜백 함수) - 응답(response)을 받았을 때의 콜백 함수결과를 받았을 때의 콜백 함수를 정의합니다.

     

     

    3. 외부 라이브러리 방식

    jquery를 사용하는 경우 jquery 안에 $.ajax() 함수가 존재하고,

    React/Vue 환경 같은 경우 axios 라이브러리를 설치해서 많이 씁니다.

     

     

     

    Ajax를 활용한 중고차 홈페이지 제작하기

    (JS_workspace_NodeJS_ajax 폴더)

     

    웹서버 환경 준비 (server.js)

    // express 서버
    const http = require('http');
    const express = require('express');
    const app = express();
    const cors = require('cors');
    const bodyParser = require('body-parser');

    require('파일경로') - Nodejs에서 외부 모듈을 가져올 때 사용하는 메서드.

    • http, express, cors, body-parser 모듈(패키지)을 불러옵니다.

     

    // 새로운 속성 추가
    app.set('port', 3000); // 서버가 실행 될 포트 지정
    app.set('views', __dirname + '/public'); // view 경로 설정 (화면에 띄울 폴더 경로)
    app.set('view engine', 'ejs'); // 확장자. 화면 엔진을 ejs로 설정

    app.set( ) - 이름과 값을 할당하는 데 사용합니다. 서버의 동작을 구성할 수 있습니다.

     

    // 미들웨어 추가
    app.use(cors());
    app.use(express.static(__dirname + '/public'));
    app.use(bodyParser.urlencoded({extended: true}));
    app.use(bodyParser.json());

    cors( ) - 교차 출처 리소스 공유(Cross-origin resource sharing). 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에게 알려주는 체제입니다. 이를 허용해주지 않으면 다른 url과 통신할 수 없습니다.

    •  ex) CORS 설정을 하지 않는 경우 현재 서버에서 kakao.com으로 이동하려고 하면 차단 됩니다.

    app.use(express.static( ) - 정적 파일을 제공합니다.

    • 정적 파일 : 직접 값에 변화를 주지 않는 이상 변하지 않는 파일. image, css, js 파일 등.
    • express 변수에는 static이라는 메서드가 포함 되어있습니다.
    • 이 메서드를 미들웨어로서 로드합니다.
    • public이라는 디렉터리 밑에있는 데이터들은 웹브라우저의 요청에 따라 서비스를 제공해줄 수 있습니다.
    • ex) 사용자가 127.0.0.1:3000/images/cat.jpg 로 접근한다면, 해당 파일을 public/images/cat.jpg에 존재하는지 검색
    • 두 폴더를 허용하고 싶은 겨우 두 번 사용하면 됩니다.

    bodyParser - 파싱이란, 데이터를 내가 원하는 형태로 '가공'하는 과정을 뜻합니다. 파서란, 그 과정을 수해하는 모듈or메소드를 말합니다.

    • 미들웨어 없이 request.body에 접근하는 경우 기본으로 undefined 설정 됩니다.
    • 따라서 bodyParser 등과 같은 미들웨어를 사용하여 요청 데이터 값에 접근해야 합니다.
    • 클라이언트 측에서 post, put 요청 시 body를 포함하여 보내는데, 이 값을 서버 측에서 받는다고 그대로 사용할 수 있는 게 아니라 서버 내에서 해석 가능한 형태로 변형해야 사용 가능해집니다.
    • 이때 api 요청에서 받은 body 값을 파싱하는 역할을 수행하는 것이 bodyParser입니다.
    • 결론적으로, put/post 요청 사용하고 싶으면 이 미들웨어를 추가해주면 됩니다.

    (*미들웨어 - 양 쪽을 연결하여 데이터를 주고 받을 수 있도록 중간에서 매개 역할을 하는 소프트웨어.)

    // http 서버 생성
    const server = http.createServer(app);
    server.listen(app.get('port'), ()=>{ // 연결 요청 대기 (몇 번 포트에서 서버를 실행할 지 지정)
        console.log('서버 실행 중 : http://localhost:3000');
    });

     

     

     

    UI 구현 ( /public/index.html )

    • html <body> 부분

    <body class="container">
        <h1>중고차 쇼핑몰</h1>
        <!-- 입력 영역 -->
        <form action="#" id="carForm">
            <table class="table table-bordered"> <!-- bootstrap 사용 -->
                <tr>
                    <th>차종</th>
                    <td><input type="text" name="name" value="TEST"></td>
                </tr>
                <tr>
                    <th>가격</th>
                    <td><input type="text" name="price" value="15000"></td>
                </tr>
                <tr>
                    <th>회사</th>
                    <td><input type="text" name="company" value="TESLA"></td>
                </tr>
                <tr>
                    <th>연식</th>
                    <td><input type="text" name="year" value="2019"></td>
                </tr>
                <tr>
                    <th colspan="2">
                        <input type="submit" value="등록">
                    </th>
                </tr>
            </table>
        </form>
    
        <!-- 검색 영역 -->
        검색 : <input type="text" id="searchInput" placeholder="검색어 입력">
        <input type="button" value="검색" id="searchBtn">
    
        <!-- 목록 영역 -->
        <div id="listBox">
            <table class="table table-bordered"> <!--listBox.children[0]-->
                <thead>
                    <th>번호</th>
                    <th>차종</th>
                    <th>가격</th>
                    <th>제조사</th>
                    <th>연식</th>
                    <th>별점</th>
                    <th>삭제</th>
                </thead>
                <tbody id="listBody"></tbody>
            </table>
        </div>

     

    • <script> 부분

    아래 carList를 동적으로 보여준다.

     

    <!-- 언더 스코어 사용 -->
    <script src="https://unpkg.com/underscore@1.13.4/underscore-umd-min.js"></script>
        <script>
            const template = `<%
                carList.forEach(function(car) {
            %>
                <tr class="listBoxRow">
                    <td><%=car.cno %></td>
                    <td><%=car.name %></td>
                    <td><%=car.price %></td>
                    <td><%=car.company %></td>
                    <td><%=car.year %></td>
                    <td>
                        <select class="star">
                            <option>1</option>
                            <option>2</option>
                            <option selected>3</option>
                            <option>4</option>
                            <option>5</option>
                            </select>
                    </td>
                    <td><button>delete</button></td>
                </tr>
            <%
                });
            %>`;
    
            let carList = [];
    
            myGetList("/list");
    
            function myGetList(url) {
                let xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function(){
                    if(xhr.status==200 && xhr.readyState==4) {
                        let text = xhr.responseText;
                        carList = JSON.parse(text).carList // JSON 구문 분석
                        drawTable(carList);
                    }
                }
                xhr.open("get", url, true);
                xhr.send();
            }

     

    carList = JSON.parse(text).carList data 를 콘솔에 출력하여 확인하면 아래와 같습니다.

     

    JSON.parse() 실행 결과

     

     

     

    테이블 그리기

    function drawTable(carList) {
                var render = _.template(template); // 템플릿 함수 반환
                var html = render({carList}); // 변수가 담긴 JSON 객체를 넘겨주어 컴파일 하면 최종 결과물 리턴
                var tbody = document.querySelector("#listBox tbody");
                tbody.innerHTML = html;
    
                storeStar();
            }


    Underscore.js
    - 자바스크립트 라이브러리. 모든 함수들 앞에 _ 사용(마치 jQuery가 $를 사용하듯이)

    • 템플릿을 텍스트로 정의해두고, <% %><%= %> 같은 특수 태그 안에 템플릿 관련 코드와 변수를 선언합니다.
    • JSON 객체를 넘겨 컴파일 하면 최종적인 결과 문자열이 반환 됩니다.
    • <%= 변수명 %> : 변수 값을 출력하는 출력문
    • <% %> 반복문 같은 템플릿 전용 문법을 작성할 때 사용

    var html = render({carList})

     

     

    템플릿 함수에 JSON 객체를 넘겨주면 이렇게 예쁜 html문을 만들어줍니다!

    이걸 tbody.innerText 를 사용하여 넣어주면 됩니다.

     

     

     

    검색창 키보드 이벤트 리스너

     var searchInput = document.querySelector("#searchInput");
            searchInput.addEventListener('keyup', (e)=> {
                var keyword = e.currentTarget.value;
                var searchList = finder2(carList, keyword);
                drawTable(searchList);
            });

    검색란에 키보드 이벤트 리스너를 추가합니다.

    's'를 검색하면 s 가 들어가는 모든 carList 키워드를 가져와야 합니다.

    finder2 함수에 carList와 눌린 키 값을 인자로 보내줍니다.

     

     

     

    finder2 함수

    function finder2(carList, keyword) {
                var searchList = carList.filter((car)=>{
                    var idx = (car.name.toLowerCase()).indexOf(keyword.toLowerCase());
                    return idx != -1;
                });
                return searchList;
            }

    carList.filter((car)=>{콜백함수}) - filter 함수는 배열 carList를 순회합니다.

    • 콜백함수(element, index, Array) - 요소값, 요소 인덱스, 사용되는 배열 객체
    • 현재 순회하는 배열(carList)의 인자값이 car 에 들어간다.

    indexOf( ) - 특정 문자 위치를 찾습니다.

    • carList의 각각 요소에서 검색한 키워드가 위치한 인덱스를 반환한다.
      • -1 이 아니면(즉, 검색 키워드가 포함되면) 해당 요소를 반환한다.

     

    s를 검색했을 때 searchList

     

     

     

    중고차 등록하기

     let carForm = document.getElementById("carForm");
    carForm.addEventListener("submit", (e)=>{
        e.preventDefault();
        let name = e.currentTarget.name.value;
        let price = e.currentTarget.price.value;
        let company = e.currentTarget.company.value;
        let year = e.currentTarget.year.value;
    
        console.log(name, price, company, year);
    
        let xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if(xhr.status==200 && xhr.readyState==4) {
                let text = xhr.responseText;
                carList = JSON.parse(text).carList
                drawTable(carList);
            }
        }
        xhr.open('post', "/insert", true);
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        let queryString = `name=${name}&price=${price}&company=${company}&year=${year}`;
        console.log(queryString);
        xhr.send(queryString);
    });

    xhr.setRequestHeader(헤더이름, 헤더값) - HTTP 요청 헤더의 값을 설정합니다.

    • setRequestHeader 전에 반드시 open()을 호출하고, 그 이후에는 send() 메소드 적어주어야 합니다.
    • open() 함수를 통해서 http 요청 방식과 요청 url을 정의했다고 바로 실행되는 것이 아닙니다.
    • 클라이언트에서 서버로 http요청을 하기 전에 요청에 맞는 헤더 값을 설정해야 합니다.
    • 서버로 전송하는 데이터(body)의 타입을 선언하기 위한 용도로 만히 사용 됩니다.
    • content-type : 서버로 전송할 데이터의 MIME 타입 정보 설정
      • <MIME 타입 종류> 
      • 1. application/json - Restful API 사용하게 됨, {key:value}의 형태로 전송
      • 2. text/plain
      • 3. multipart/form-data
      • file을 실어보내기 위한 타입
        • multipart/form-data
        • application/x-www-urlencoded - html form의 기본 content-type. 'key=value&key=value'의 형태로 전송.

     

     

     

     

    /insert url로 post 요청을 보냈을 때 server.js에서는...

    app.post("/insert", (req, res)=>{
        let carData = {
            cno : top++,
            name : req.body.name,
            price : req.body.price,
            company : req.body.company,
            year : req.body.year,
            star: 3
        };
        carList.push(carData);
        res.send({carList});
    });

    클라이언트가 보낸 데이터(body)의 각 키 값을 carData에 저장합니다.

    해당 carData를 carList에 추가하고, 클라이언트가 입력한 데이터가 추가 된 carList를 응답으로 보냅니다.

     

    그러면 클라이언트는 위 응답을 받아 JSON으로 전환 후 새로 drawTable(carList)을 합니다!

     

     

Designed by Tistory.