[restify] Aws mysql로 db 설계 후 rds, redis 연동하고 간단한 api 구성

리진아·2023년 8월 23일
0
post-thumbnail

먼저 db 설계를 하고 rds를 생성한 후 mysql에서 엔드포인트에 연결한다.
그런 다음 redis를 설치 후 서버를 킨다.
준비가 끝났으면 api를 만들어준다.



Table 설계

웹툰 사이트의 취지에 맞게 간단하게 설계했다. 이후 계속 수정하느라 필드는 약간 다를 수 있다.


rds 생성

aws에서 iam 사용자를 만든 후 rds 권한을 부여한다.

rds 생성은 이 블로그를 참고하였다.
https://makethree.tistory.com/6

rds는 mysql로 db.t3.micro로 설정해야 프리티어를 사용할 수 있다.

aws 요금 이슈

처음에 rds와 ec2 인스턴스 모두 버지니아 북부에 생성했었다. 배포를 한 후 네트워크 속도가 3배 가까이 느려졌다. (redis를 사용하는 이유가 없어짐.) 알고보니, 지역이 서울이 아니였기 때문이었고, rds를 새로 만드는 것이 아닌 스냅샷으로 옮기느라 aws에서 비용이 추가되었다.

지역이 중요한지 모르고 대충 생성하니까 이런 문제가 생기다니..

생성할 때 리전은 반드시 서울로 하자!


mysql 워크벤치로 연결했다. 테이블을 생성하고, 임시 데이터들을 넣어놓았다.


db 연결

모듈 설치 후

npm install mysql2

rds에서 준 엔드포인트를 가지고 db를 연동한다

//database 관련 코드

const getConn = async () => {
    return await pool.getConnection(async (conn) => conn);
};

//데이터베이스 연동
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
    host : 엔드포인트
    port : '3306',
    user : 'admin',
    password : 'abcd1234',
    database : 'webtoon'
});


module.exports = { getConn };

Redis 실행

설치할 수 있는 가이드는 많기에 생략.

터미널에서 redis-server

서버가 잘 구동이 되었다.

코드로 프로젝트와 연결해주자

// redisApi.js

const redis = require('redis');

//redis 클라이언트 초기화
const redisClient = redis.createClient({
    host: "127.0.0.1",
    port: "6379",
    db: "0"
});

//redis 클라이언트 연결
redisClient.connect((err) => {
    if (err) {
        console.error(err);
    } else {
        console.log('연결 됨');
    }
});

module.exports = redisClient;

연결이 잘 되었다.

❗️에러 ClientClosedError

ClientClosedError: The client is closed

계속 위와 같은 에러가 났는데 터미널에 redis-cli를 안 열어서 생기는 에러인 줄 알았다... 아래 코드 추가하니 사라진 에러

redisClient.connect((err) => {
    if (err) {
        console.error(err);
    } else {
        console.log('연결 됨');
    }
});






간단한 api로 db ,redis 연결 테스트

웹툰 전체 정보를 확인할 수 있는 api를 만들었다.

//controller
    // 웹툰 정보
    async getWebtoons(req, res) {
        try {
            // All 또는 요일
            const { pi_vch_condition } = req.query;
            const webtoon = await WebtoonService.getWebtoons(pi_vch_condition);
            res.send(webtoon); // 웹툰 내용
        } catch (error) {
            console.error(error);
            res.status(500).json({ message: '서버 오류' });
        }
    },
//Route
    // 웹툰 정보
    server.get('/api/webtoons', WebtoonController.getWebtoons);
//Service
    //웹툰 정보 출력
    async getWebtoons(pi_vch_condition) {
        const conn = await getConn();

        try {
            const Webtoonkey = `webtoon : ${pi_vch_condition}`;
            let value = await redisClient.get(Webtoonkey);

            if (value) {
                return JSON.parse(value);
            } else {
                const [result] = await conn.query('CALL usp_get_Webtoons();');
                const webtoon = result[0];

                // 좋아요 redis에 저장
                for (let i = 0; i < webtoon.length; i++) {
                    //console.log(webtoon[i]);
                    const item = webtoon[i];
                    const likeKey = `likes:${item.webtoonID}`;
                    const likeValue = await redisClient.get(likeKey);

                    if (likeValue) {
                        item.likes = JSON.parse(likeValue); // 이미 저장된 좋아요 정보 사용
                    } else {
                        await redisClient.set(likeKey, item.totalLikes);
                        item.likes = item.totalLikes; // 새로운 좋아요 정보 저장
                    }
                }

                if (pi_vch_condition === 'All') {  
                    await redisClient.set(Webtoonkey, JSON.stringify(webtoon)); 
                    await redisClient.expire(Webtoonkey, 3600); //webtoon : All 키 1시간마다 삭제

                    return webtoon; // 응답으로 모든 웹툰 정보

                }else { // 요일별 웹툰
                    const result = webtoon.filter((item) => item.webtoonWeek === pi_vch_condition); //요일이 같은 것만 출력
                    await redisClient.set(Webtoonkey, JSON.stringify(result));  // 저장
                    return result; // 응답으로 모든 웹툰 정보
                }
            }
        } catch (error) {
            throw error;
        } finally {
            conn.release();
        }
    },




파라미터로 요일을 받으면 db에서 데이터 값을 가지고 온 후 redis의 키 값과 value 값을 저장하고 1시간 후면 값이 사라지도록 설정해놓았다.

웹툰의 좋아요를 redis에 따로 저장하기 위해 루프를 돌려 likes:{webtoonID} 값을 저장하였다.


DELIMITER //
CREATE PROCEDURE usp_get_Webtoons()
BEGIN
    SELECT 
        WebtoonTable.webtoonID,
        WebtoonTable.webtoonName,
        WebtoonTable.webtoonEnName,
        WebtoonTable.webtoonAuthor,
        WebtoonTable.webtoonContent,
        WebtoonDetailTable.webtoonThumbnail,
        WebtoonDetailTable.webtoonWeek,
        WebtoonTable.webtoonDate,
        -- null일땐 0으로
        IFNULL(like_counts.totalLikes, 0) AS totalLikes
    FROM WebtoonTable
    JOIN WebtoonDetailTable ON WebtoonTable.webtoonID = WebtoonDetailTable.webtoonID
    LEFT JOIN ( -- 좋아요 갯수 계산해서 조인함
        SELECT webtoonID, SUM(likes) AS totalLikes
        FROM LikeTable
        GROUP BY webtoonID
    ) AS like_counts ON WebtoonTable.webtoonID = like_counts.webtoonID;
END //
DELIMITER ;

usp_get_Webtoons 프로시저이다. 실행하면 아래와 같은 결과가 나온다.




이제 api 경로를 호출해보면

리턴 값이 잘 나온다.




redis-cli를 통해 key와 value 값을 확인할 수 있다.

key와 value가 잘 저장이 되어있다.






클라이언트에서 만든 화면이다.
오직 위에서 만든 api를 사용하여 만들었다.

profile
알맹이가 가득 찬 개발자가 되기 위해 한 걸음 더 다가가는,

0개의 댓글