오늘은 EPL 리그 성적 업데이트를 하는 것 만들었습니다. 
간단한 API를 만들 때 플로우나 어떤 데이터를 가지고 가공을 해야하는지 정리해두고 하는게 일을 효과적으로 빠르게 처리할 수 있습니다. (개인적인 생각)

목적 

- 크롤링 해온 데이터와 기존의 DB 정보에서 오늘 진행한 경기가 끝날 때마다
  (스케줄러{현재 - 24:00 기준으로 진행중}) 클럽별 총 전적을 업데이트함.

PremierLeaugeData :  EPL 리그에 대한 상세 내용이 기재되어 있는 사이트에서 데이터를 크롤링해서 필요한 정보만 가져옵니다.
DatabaseData : 데이터베이스 내에 업데이트 되어야하는 테이블에서 기본키값과 클럽ID, 클럽명(LEFT JOIN 클럽) 을 가져옵니다. 

let query = "INSERT INTO [테이블 명] [컬럼들]... VALUES";
for (const [index, premierLeagueData] of premierLeagueList.entries()) {
  for (const clubData of clubList) {
    if (clubData.club_name.includes(premierLeagueData.team)) {
      query += `(${clubData.participating_club_id}, ${premierLeagueData.ranking}, ${premierLeagueData.totalMatches}, ${premierLeagueData.win}, ${premierLeagueData.draw}, ${premierLeagueData.lose}, ${premierLeagueData.score}, ${premierLeagueData.conceded}, ${premierLeagueData.gainLossDifference}, ${premierLeagueData.point}, "${premierLeagueData.last5Matches}")`;
      // 구분을 해주지 않으면 구문 에러가 납니다!!
      if (index + 1 !== premierLeagueList.length) {
        query += ',';
      }
    }
  }
  
query += ` ON DUPLICATE KEY UPDATE 
  				ranking = VALUES(ranking) ...

return query;

Q) 데이터 가공되어질 때마다 query를 DB에 보내면 되지 않나요?
A) 저는 데이터베이스 연결을 요청하는 작업이 많은 자원을 소모하기 떄문에, 한번에 query를 만들어서 보내는게 좋다고 생각했습니다.

Q) INSERT INTO ... ON DUPLICATE KEY UPDATE 가 아니라 UPDATE 구문을 사용해도 되는 것 아닌가요?
A) 저도 성능을 직접 측정해보지 않아서 정확하게는 모르겠습니다. 추후에 정리한 뒤 어떤 것이 더 효율적인지 비교하려고합니다.

'개발일지 > 설계' 카테고리의 다른 글

[Node.js] 채팅 설계 Q&A  (2) 2024.02.14

사이드 프로젝트 진행중 ... 안드로이드 개발자(기획)와 백엔드 개발자의 채팅 설계 질문지에 대한 답변 정리입니다.

Q) 1:1 채팅 앱인지, 그룹 채팅 앱인지?

A) 그룹 채팅입니다. 생각했던 스토리는 경기 시작 1시간 전에 채팅방 입장이 가능하고(나중에 푸시와 연동) 경기 종료 1시간 후에 채팅방이 끝나는 느낌이구요, 그 때 해당 경기에 관해 채팅을 할 수 있는 방이 하나만 열리고, 그 방에서 그룹 채팅을 하는 형태입니다.

Q) 일별 능동 사용자 수 (DAU) 기준으로 몇명을 처리할 예정인지?

A) 아직 고려는 하지 않았습니다. 몇 명인지에 따라서 개발 방향이 크게 바뀐다면 한번 다같이 더 찾아보고 의논해보는 게 좋을 것 같습니다.

Q) 그룹 채팅의 경우에 인원 제한이 있는지?

A) 따로 두지는 않았습니다. Socket 통신 시 문제가 생기는 지점이 있는지 파악을 해두는 건 좋을 것 같습니다. 서버단에서 체크를 해보는 것도 좋을 것 같아요. 

Q) 중요 기능으로 어떤 것이 있는지, 첨부파일도 지원할 수 있는지?

A) 저희가 따로 파일을 저장하는 시스템이 현재 없어서 첨부파일은 고려하지 않아도 될 것 같구요. 간단하게 생각해본 건 챗봇 느낌으로 명령어나 특정 메세지를 보내면 그에 맞게 데이터를 보내주고 클라 UI 상에서 일반 메시지와는 다르게 보여준다거나, 경기와 관련된 채팅이기 때문에 득점이나 경기 시작, 종료 등과 같은 상황을 브로드캐스트로 뿌려준다거나 하는 등의 기능을 생각해봤습니다. 우선은 복잡하게 데이터 규격을 정해야 하는 기능보다는 문자열 형태로도 주고받을 수 있는 기능인데, 다른 형태로 구현해 볼만한 기능이 있는지도 고민해보겠습니다.

Q) 메시지 길이에 제한이 있는지?

A) 당장은 변수가 발생할 수도 있는 상황을 배제하고 원활한 처리를 위해 길이를 제한하는 게 좋을 것 같구요, 현재는 메시지 저장을 따로 안 하는데 차후에 더 디벨롭해서 기능을 확장하게 되어 저장을 한다면 테이블 구조와 소켓 통신, 클라에서 처리 가능한 길이 등을 고려해서 제한을 두는 것도 좋을 것 같습니다.

Q) 종단 간 암호화를 할 것 인지?

A) 메시지 저장하기 전까지는 고려하지 않아도 될 것 같습니다.

Q) 채팅 이력은 얼마나 오래 보관해야하는지?

A) 지금 기능 상으론 서버에선 소켓으로 들어온 메세지를 바로바로 다시 내보내는 형태일 걸로 생각되는데 그 때 따로 보관은 하지 않아도 될 것 같구요. 클라에선 채팅방 입장~퇴장 까지 중에서 화면이 활성화되어 있는 상황에서 주고 받은 메세지들은 보여줘야 하기 때문에 메모리상에는 들고 있을 것 같습니다. 서버나 클라나 현재로선 메시지를 DB에 저장하지는 않아서 관련 기능 추가전까지는 고려하지 않아도 될 것 같습니다.

프로젝트 GITHUB : https://github.com/Mirandalaw/gooner

 

Node.js로 데이터베이스 커넥션 풀을 구현하면서 사용하게 된 라이브러리를 정리하기 위해 작성합니다.

[generic-pool]

- Node.js에서 일반적인 자원 풀링을 구현할 수 있도록 도와주는 라이브러리임.
- 이 라이브러리를 사용하면 자원( DB connection, HTTP 요청 등) 을 관리하고 재사용할 수 있음.
- 특히, 데이터베이스 연결과 같은 생성 비용이 비싼 자원을 사용할 때 효과적임.

사용법
 1. 풀 생성 및 구성 : "generic-pool" 을 사용하여 풀을 생성하고 필요한 옵션을 구성함.
 2. 자원 생성 및 반환 로직 정의 : 풀에서 관리할 자원을 생성하고 반환하는 로직을 정의함. ( 이는 "create" 와 "destroy" 옵션에 해당함)
 3. 자원 획득 및 반환 : "acquire" 함수를 사용하여 자원을 획득하고, 작업이 끝난 후에 "release" 함수를 사용하여 자원을 풀에 반환함.

사용이유
 1. 성능향상 : 자원을 반복적이고 생성하고 소멸하는 것은 비용이 많이 들며 시스템 성능을 저하시킬 수 있음.
대신 자원을 풀에 보관하고 필요할 때 재사용함으로써 성능을 향상시킬 수 있음.
 2. 자원관리 : 풀을 사용하면 동시에 여러 작업에서 자원을 공유하고 관리할 수 있음. 이는 메모리 누수나 자원 고갈과 같은 문제를 방지할 수 있음.
 3. 서버 부하 감소 : 풀을 사용하면 서버에 대한 부하를 분산할 수 있음. 
예를 들어, 데이터베이스 연결 풀을 사용하면 동시에 많은 클라이언트 요청을 처리할 수 있고, 데이터베이스 서버에 대한 과도한 부하를 방지할 수 있음.
 4. 애플리케이션 확장성 : 풀을 사용하면 애플리케이션의 확장성을 향상시킬 수 있음.
풀을 관리하는 컴포넌트를 쉽게 확장하거나 교체하여 시스템의 요구 사항에 맞게 조정할 수 있음.
 5. 안정성 : 풀을 사용하면 시스템이 과부하 상태일 때도 자원을 효율적으로 관리할 수 있음. 이는 시스템의 안정성을 높일 수 있음.

자원 풀링
- 다수의 요청이 동시에 발생하는 경우, 요청마다 자원을 새로 생성하는 것이 아니라 풀에 미리 생선된 자원을 할당하여 사용하고,
작업이 끝나면 자원을 풀에 반환하는 방식으로 동작함. 
=> 이를 통해 성능을 향상시킬 수 있음.

 

 유지보수 도중, 기존의 Row Query가 이해가 안가는 부분이 있어 생각하고 직접 사용해보며, 그 차이를 정리하고자합니다.

"INSERT ... ON DUPLICATE KEY UPDATE"  는 MySQL에서 사용되는 쿼리 구문
특정한 유니크 또는 프라이머리 키 제약 조건을 가진 테이블에 데이터를 추가하려고 할 때, 
이미 해당 키가 존재하면 기존 행을 업데이트하고 그렇지 않으면 새로운 행을 삽입함.
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...)
ON DUPLICATE KEY UPDATE column1 = value1, column2 = value2, ...;

- table_name: 데이터를 삽입할 테이블의 이름
- (column1, column2, ...): 삽입될 열의 이름 목록
- (value1, value2, ...): 삽입될 값의 목록
- ON DUPLICATE KEY UPDATE: 유니크 또는 프라이머리 키 충돌 시 실행되는 업데이트 부분
- column1 = value1, column2 = value2, ...: 충돌 시 각 열에 대해 업데이트할 값을 지정

"INSERT IGNORE" 는 MySQL에서 사용되는 쿼리 구문, 데이터를 특정 테이블에 삽입하고 할 때,
유니크 또는 프라이머리 키 충돌 등의 이유로 삽입이 불가능한 경우 해당 행을 무시하고 계속 진행함.
INSERT IGNORE INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);

- table_name: 데이터를 삽입할 테이블의 이름
- (column1, column2, ...): 삽입될 열의 이름 목록
- (value1, value2, ...): 삽입될 값의 목록
- IGNORE: 유니크 또는 프라이머리 키 충돌이 발생하더라도 해당 행을 무시하고 진행


어떤 것을 어느 상황에서 사용해야할까요?
성능적인 측면에서 봤을 땐, 어떤 것이 더 좋을까요?

"INSERT ... ON DUPLICATE KEY UPDATE"
 - 중복된 키 충돌이 발생했을 때 업데이트를 수행하는 경우
 - 특정 열을 업데이트하거나 추가적인 처리를 수행하는 경우
 - 중복이 발생할때 마다 추가적인 업데이트 작업이 수행되므로 오버헤드가 발생할 수 있음

"INSERT IGNORE"
 - 중복된 키 충돌이 발생했을 때 무시하고 계속 진행하는 경우
 - 추가적인 업데이트 또는 변경이 필요하지 않은 경우
 - 중복 발생 시 단순히 무시하므로 추가적인 작업이 없어 성능이 위에보다 좋을 수 있음

안녕하세요.
오늘은 Redis를 이용한 AccessToken과 RefreshToken 관리 방법에 대해 정리하려고합니다.

[기존의 제가 사용하던 방식]

유저가 로그인을 하는 경우
AccessToken은 Response로 Client (Front or Android...) 에게 전달
RefreshToken은 Redis에 저장

이렇게 한 이유
"RefreshToken이 RDB(MySQL, ...) 에 저장되면 Session과 뭐가 다른게 있을까?" 생각 때문이었습니다.
만약, RefreshToken이 RDB에 저장되는 경우
AccessToken이 만료가 되는 경우, RefreshToken을 DB에 접근을 하면서 DB자원을 사용하는 것이 마음에 들지 않아...
Redis를 사용하였습니다.

고치게 된 이유
정리를 하는 중에, 이 방식도 보안적으로 문제가 있다고 생각이 들었습니다.
이 주제로 고민과 주변 개발자분들에게 조언을 얻어...



[새로운 방식]

유저가 로그인을 하는 경우
AccessToken은 Response로 Client (Front or Android...) 에게 전달.
AccessToken은 Redis에 저장.
RefreshToken은 암호화하여 RDB에 저장.
만약, AccessToken이 만료되는 경우
RefreshToken은 RDB에서 조회 유효한지 확인.
RefreshToken이 사용되면 새로운 RefreshToken이 발급되어 RDB에 저장.

이렇게 한 이유
기존 사용하던 방식보다는 안전한 방식이라고 생각이 들었습니다.


간략하게 정리...

AccessToken, RefreshToken 란?

AccessToken (액세스 토큰) 
- 역할 : 사용자가 인증된 후 서비스에 접근할 때 사용되는 토큰.
- 수명 : 보통 짧은 수명을 가지며, 특정 시간이 지나면 만료됩니다.
- 획득 방법 : 사용자가 로그인하면 서버에서 발급되고, 이를 클라이언트가 HTTP 헤더 등에 실어서 API 요청 시 함께 보내어 인증함.

RefreshToken(리프레시 토큰)
- 역할 : AccessToken이 만료되었을 때 새로운 AccessToken을 발급받을 수 있는 권한을 부여하는 토큰.
- 수명 : AccessToken보다 상대적으로 긴 수명을 가지며, 안전한 방식으로 저장되어야함.
- 획득 방법 : 사용자가 로그인 시 함께 발급되며, AccessToken이 만료되었을 때 서버에 보내져 새로운 AccessToken을 받음.

Redis 란?

Redis
- 역할 : 데이터를 캐싱하고 저장하기 위한 오픈 소스 인메모리 데이터 구조 저장소임.
- 활용 : 토큰, 세션, 캐시 등을 저장하는 데 사용됨.
- 특징 : 빠른 속도와 메모리 기반이기 때문에 인증 토큰을 안전하게 저장하고 빠르게 검색할 수 있는 용도로 활용됨.

 실무를 하다보니.. 새로운 것을 만들기보다는 기존에 있던 것을 유지보수 하거나 기능을 추가하는 일이 많습니다.
아무래도 새로운 모듈을 추가하거나 새로운 것을 처음부터 개발하는 경우는 많이 없었습니다.
 그래서 잊고지내던 친구들을 다시 보고싶어 정리를 결심하게 되었습니다.

1. Node 프로젝트의 'devDependencies' 와 'dependencies' 의 차이.

   1) dependencies (의존성) 
     : 이 카테고리에 있는 패키지는 프로덕션 환경에서 실행될 때 필요한 패키지입니다.
     즉, 실제로 애플리케이션이 동작할 때 사용됩니다. 이러한 패키지들은 애플리케이션의 핵심 로직을 지원하거나 실행에 필수적인 라이브러리임.

    -> 이게 뭔말이냐.. npm install express 를 하면 express 모듈을 사용해서 서버를 구축하잖아요?
그럼 이건 프로덕션 환경(실제 서비스) 에서 사용되는 것이기 때문에 dependencies 에 속하게 되는 거죠..

  2) devDependencies (개발 의존성)
    : 이 카테고리에 있는 패키지는 주로 개발 환경에서 개발자의 작업을 돕거나 테스트, 빌드, 디버깅 등을 지원하는 도구나 라이브러리임. 
    즉, 이러한 패키지들은 실제 프로덕션 환경에서 필요하지 않으며, 주로 개발자가 코드를 작성하고 테스트하는 동안 사용됨.

  -> 이건... 많이들 사용하시는 npm install rand-token  nodemon --save-dev 를 사용하는 경우죠.
그럼 대개 뭐가 있냐.. 위에서 install 한 라이브러리들이 대개 개발자의 작업을 돕는 도구나 라이브러리가 되는 것이죠.

'개발일지 > NODE' 카테고리의 다른 글

[Node.js] generic-pool  (0) 2024.02.11
[Node.js] 에러핸들러 만들어보기  (0) 2023.04.21
[Node.js] 비밀번호 암호화 하기  (0) 2023.04.20
[Node.js] JWT 에러 핸들링  (0) 2023.04.19

+ Recent posts