메타버스 백엔드 웹반

[Node.js] Express.js와 MySQL로 구현하는 CRUD 기능: 조회, 삭제, 수정 및 RESTful API

thisnorm 2025. 1. 20. 22:56

1. 조회 READ(select문) 기능

app,js의 조회 API 작성

우선 READ기능인 select 쿼리문을 실행할 수 있는 로직이 담긴 ‘/contactList’ 엔드포인트의 GET요청 API를 작성한다.

app.get('/contactList', (req, res) => {
    const selectQuery = `select * from contact order by id desc`;

    // 얻어온 커넥션을 사용하여 쿼리를 실행합니다.
    connectionPool.query(selectQuery, (err, result) => {
        if (err) {
            console.error('데이터 조회 중 에러 발생:', err);
            res.status(500).send('내부 서버 오류');
        } else {
            console.log('데이터가 조회되었습니다.');
            console.log(result);
            res.render('contactList', {lists:result});
        }
    });
});

쿼리 결과로 result를 반환하며 lists라는 변수에 담아서 contactList.ejs로 render되도록 작성한다. 아래는 조회 결과인 result에 담긴 데이터이다.


목록 뷰 페이지 contactList.ejs

등록된 문의사항을 볼 수 있는 페이지를 만들어보자.

API에서 lists에담긴 데이터들은 forEach를 통해 테이블을 통해서 데이터들을 순회하면서 1개의 데이터(row)마다 tr태그를 생성하도록 설정한다. ejs 템플릿의 반복문 및 대입 문법인 <% … %>에서 forEach와 =대입연산자를 활용해야 한다.

<%-include('layouts/header.ejs')%>
    
    <h2>문의 목록입니다.</h2>

    <table border=1>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>phone</th>
            <th>email</th>
            <th>content</th> 
            <th>create_at</th> 
            <th>modify_at</th> 
        </tr>
        <% lists.forEach(function(item) { %>
        <tr>
            <td><%=item.id%></td>
            <td><%=item.name%></td>
            <td><%=item.phone%></td>
            <td><%=item.email%></td>
            <td><%=item.memo%></td>
            <td><%=item.create_at%></td>
            <td><%=item.modify_at%></td>
        </tr>
        <% }) %>
    </table>

<%-include('layouts/footer.ejs')%>

서버를 실행하여 contactList 엔드포인트의 API로 요청하면 다음과 같이 데이터베이스의 자료를 조회한 데이터를 목록으로 볼 수 있다.

데이터베이스에서 쿼리로 조회한 자료와 같은 것을 확인 할 수 있다.


2. 삭제 DELETE 기능

app.js에 삭제 API 추가

각 문의사항마다 삭제할 수 있는 기능을 추가하고자 한다. 우선 삭제 쿼리문 로직이 담긴 API를 작성한다. 특정 게시글을 삭제 할 수 있도록 url PathVariable로부터 id 값을 받아와서 변수에 할당해두고 delete 쿼리문에서 where절에 해당 id를 특정하여 삭제한다.

app.post('/api/contactDelete/:id', (req, res) => {
    const id = req.params.id;
    const deleteQuery = `delete from contact where id='${id}'`;
    connectionPool.query(deleteQuery, (err, result) => {
        if (err) {
            console.error('데이터 삭제 중 에러 발생:', err);
            res.status(500).send('내부 서버 오류');
        } else {
            console.log('데이터가 삭제되었습니다.');
            res.send("<script>alert('문의사항이 삭제되었습니다.'); location.href='/contactList'</script>");
        }
    });
});

뷰페이지에서 삭제 요청 추가 contactList.ejs

우선 HTML 테이블에 각 글마다 버튼 링크가 생성되도록 수정한다. 링크에 글의 고유값(PK)인 id를 활용하여 해당 글을 특정 할 수 있도록 한다. 삭제의 confirm 이벤트를 추가하여 1차적인 확인 과정을 추가해둔다.

...

            <th>DELETE</th> 

...

            <td><%=item.modify_at%></td>
						<td>
                <form action="/api/contactDelete/<%=item.id%>" method="post">
                    <button type="submit" onclick="return confirm('정말 삭제하시겠습니까?')">DELETE</button>
                </form>
            </td>

...

삭제 버튼의 URL을 통해 삭제 API로 요청과 id가 전달되며 아래와 같이 정상적으로 데이터가 삭제되는 것을 확인 할 수 있다.


3. 테이블 수정

우선 현재 테이블의 구조는 다음과 같다.

CREATE TABLE `contact` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `phone` varchar(20) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `memo` text,
  `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `modify_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

여기에 status라는 글의 상태를 추가하여 ongoing(진행중), done(완료) 로 문의에 대한 답변 상태를 추가하고자 한다. 따라서 문의가 생성되면 기본값으로 ongoing상태를 가져야 하며 관리자가 글의 상태를 변경(업데이트) 할 수 있도록 한다.

 

테이블에 status라는 컬럼을 추가해야 한다. 테이블을 변경하는 것이기 때문에 alter table로 시작되며 status 컬럼을 추가(add)하게 된다. default로 ongoing 값을 가지도록 설정했다.

ALTER TABLE `contact`
ADD COLUMN `status` varchar(20) DEFAULT 'ongoing';

기본값 설정에 따라 기존 모든 데이터들도 기본값이 적용되어 ongoing 상태로 나타나는 것을 볼 수 있다.


4. Update API 작성

app.js Update API 작성

post요청을 통해 id를 PathVariable로 받아온다. update 쿼리를 통해서 해당 id의 row중 status 컬럼값을 done으로 수정한다.

app.post('/api/contactUpdate/:id', (req, res) => {
    const id = req.params.id;
    const status = "done";
    const updateQuery = `UPDATE contact SET status = '${status}' WHERE id = '${id}';`;

    connection.query(updateQuery, (err, result) => {
        if (err) {
            console.error('데이터 업데이트 중 에러 발생:', err);
            res.status(500).send('내부 서버 오류');
        } else {
            console.log('데이터가 업데이트되었습니다.');
            res.send("<script>alert('문의사항이 업데이트되었습니다.'); location.href='/contactList'</script>");
        }
    });
});

contactList.ejs 요청 부분 수정

추가된 데이터의 컬럼인 status를 볼 수 있도록 테이블 컬럼을 추가하고 update버튼을 추가한다.

...
            <th>DONE</th>
            <th>DELETE</th> 
...
            <td>
                <form action="/api/contactUpdate/<%=item.id%>" method="post">
                    <button type="submit" onclick="return confirm('해당 문의를 완료 하시겠습니까?')">COMPLETE</button>
                </form>
            </td>
...

문의사항 완료 후 정상적으로 status 컬럼의 값이 수정된 것을 확인 할 수 있다.


5. REST API

HTML 폼(form) 요소는 기본적으로 **GET**과 POST 메서드만 지원하고 있다. 프론트의 form action의 method 속성을 put으로 변경하고 백엔드 라우터 또한 app.put()으로 변경해도 작동하지 않는다.

 

현재 CRUD 기능에서 Create(POST), Read(GET), Update(POST), Delete(POST)으로 구성되어 있다.

하지만 하지만 우리는 RESTful API에서 각 기능별 적절한 HTTP Method를 선택해야하기 때문에 REST API를 지향하기 위해서는 요청 메소드 자체의 문제가 있다. 일반적으로 RESTful API 디자인에서는 업데이트는 PUT 메서드, 삭제는 DELETE 메서드를 사용하는 것이 관례적이다.


Ajax요청을 위한 jQuery 참조

HTML 폼에서는 PUT과 DELETE 메서드를 지원하지 않기 때문에 외부 라이브러리를 사용해야 한다. 우선 현재까지도 활용 되는 레거시 프로젝트 중에 AJAX 라이브러리가 있다. AJAX는 전통적인 HTML <form>에서 RESTful 아키텍처로 발전하는 데 중요한 전환점이 된 모듈 중 하나다.

  • 하지만 아래와 같은 코드 처럼 아직까지 복잡해보이는 코드 구조를 가지고 있었다. 첫줄에서처럼 XMLHttpRequest 객체를 통해 처리했음
// 순수 JavaScript를 사용한 GET 요청 예시
const xhr = new XMLHttpRequest(); // XMLHttpRequest 객체 생성
const url = 'https://api.example.com/data'; // 요청할 URL

xhr.open('GET', url, true); // 요청 초기화
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) { // 요청이 완료되었을 때
        if (xhr.status === 200) { // 요청이 성공했을 때
            const data = JSON.parse(xhr.responseText); // 응답 데이터 파싱
            console.log('데이터:', data);
        } else {
            console.error('에러:', xhr.statusText); // 에러 처리
        }
    }
};

xhr.send(); // 요청 전송
  • 하지만 jQuery라는 라이브러리를 통해서 위 Ajax 요청을 더욱 쉽고 가독성 높게 아래 처럼 변경되었다. 보다 개발자 친화적으로 사용하기 편리하여 현재까지도 사용되기도 한다.
// jQuery를 사용한 GET 요청 예시
$.ajax({
    url: 'https://api.example.com/data',
    method: 'GET',
    success: function(data) {
        console.log('데이터:', data);
    },
    error: function(error) {
        console.error('에러:', error);
    }
});

jQuery를 통해 Ajax 요청문으로 활용하여 각 HTTP Method를 RESTful 하게 리팩토링 하고자 한다. 해당 라이브러리를 사용하려면, 먼저 jQuery를 프로젝트에 CDN을 통해 추가해야 한다. NPM을 통해 Node에 추가할 수도 있지만, 하지만 일반적으로 jQuery는 클라이언트 측에서 사용되는 라이브러리이기 때문에, Node.js에서 직접 사용하기보다는 웹 애플리케이션에서 클라이언트 측으로 제공하는 방식을 사용한다.

모든 페이지들의 공통 레이아웃인 header.ejs의 head 태그에 해당 코드를 작성한다.

	<!-- jQuery 추가 -->
  <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>

contactList.ejs의 수정, 삭제 ajax 요청으로 수정

버튼에 온클릭 이벤트를 통해 ajax요청을 보낼 수 있도록 설정한다. 요청 type에 ‘PUT’, ‘DELETE’로 REST API 규칙에 맞도록 작성 할 수 있다.

<%-include('header.ejs')%>
    
<h2>문의 목록입니다.</h2>

<table border="1">
    <tr>
        <th>id</th>
        <th>name</th>
        <th>phone</th>
        <th>email</th>
        <th>content</th> 
        <th>create_at</th> 
        <th>modify_at</th> 
        <th>status</th> 
        <th>DONE</th>
        <th>DELETE</th> 
    </tr>
    <% lists.forEach(function(item) { %>
    <tr>
        <td><%=item.id%></td>
        <td><%=item.name%></td>
        <td><%=item.phone%></td>
        <td><%=item.email%></td>
        <td><%=item.memo%></td>
        <td><%=item.create_at%></td>
        <td><%=item.modify_at%></td>
        <td><%=item.status%></td>
        <td>
            <button type="button" onclick="updateContact(<%=item.id%>)">UPDATE</button>
        </td>
        <td>
            <button type="button" onclick="deleteContact(<%=item.id%>)">DELETE</button>
        </td>
    </tr>
    <% }) %>
</table>

<script>
    function updateContact(contactId) {
        // AJAX를 사용하여 PUT 요청 보내기
        $.ajax({
            url: `/api/contactUpdate/${contactId}`,
            type: 'PUT',
            success: function(response) {
                // 업데이트 성공 시의 처리
                alert('문의사항이 업데이트되었습니다.');
                location.reload(); // 페이지 새로고침 또는 다른 업데이트 방식 선택
            },
            error: function(error) {
                // 오류 처리
                console.error('업데이트 오류:', error);
            }
        });
    }

    function deleteContact(contactId) {
        // AJAX를 사용하여 DELETE 요청 보내기
        $.ajax({
            url: `/api/contactDelete/${contactId}`,
            type: 'DELETE',
            success: function(response) {
                // 삭제 성공 시의 처리
                alert('문의사항이 삭제되었습니다.');
                location.reload(); // 페이지 새로고침 또는 다른 삭제 방식 선택
            },
            error: function(error) {
                // 오류 처리
                console.error('삭제 오류:', error);
            }
        });
    }
</script>

<%-include('footer.ejs')%>

API의 메소드 수정

app.js에서 app.post() 로 구성되었던 API들을 각각 용도에 맞도록 수정한다. 수정은 PUT, 삭제는 DELETE 메소드를 사용한다.

app.delete('/api/contactDelete/:id', (req, res) => {
    const id = req.params.id;
    const deleteQuery = `delete from contact where id='${id}'`;
    connectionPool.query(deleteQuery, (err, result) => {
        if (err) {
            console.error('데이터 삭제 중 에러 발생:', err);
            res.status(500).send('내부 서버 오류');
        } else {
            console.log('데이터가 삭제되었습니다.');
            res.send("<script>alert('문의사항이 삭제되었습니다.'); location.href='/contactList'</script>");
        }
    });
});

app.put('/api/contactUpdate/:id', (req, res) => {
    const id = req.params.id;
    const status = "done";
    const updateQuery = `UPDATE contact SET status = '${status}' WHERE id = '${id}';`;

    connectionPool.query(updateQuery, (err, result) => {
        if (err) {
            console.error('데이터 업데이트 중 에러 발생:', err);
            res.status(500).send('내부 서버 오류');
        } else {
            console.log('데이터가 업데이트되었습니다.');
            res.send("<script>alert('문의사항이 업데이트되었습니다.'); location.href='/contactList'</script>");
        }
    });
});

PUT과 DELETE 메소드로 수정 후 정상적으로 작동하는 기능을 확장