Server 196

대용량 쿼리 벌크 업데이트

그동안 잘못 알고 있었던 사실.DB에 최소한으로 접근하는 것이 항상 좋다.-> DB에 연결된 트랜잭션의 시간이 길어진다면, 전체적인 데이터베이스의 접근하는 속도가 저하된다. 사실 위 의미대로라면 분할로 업데이트하는 청크는 설명될 수 없다.NestJS, MySQL, Typeorm의 예를 들어보겠다. 1. 업데이트에 필요한 행만큼 쿼리문 실행async bulkMap(dto: BulkDto): Promise { let updatedCount: number = 0; await this.entityManager.transaction(async (em) => { for(const item of dto.items){ const result = await em..createQueryB..

Server/성능개선 2026.02.19

JS & TS) Map으로 시간복잡도 줄이기

C++에서는 map, set을 엄청 자주 사용했는데,JS에서는 이상하게 사용을 안하게 된다.그러다 반복문에서 include, find 등 람다식을 사용해 이중포문을 만들게 되었고. 5만건의 유저 업데이트 중, O(n2)이라는 무시무시한 경험을 해버렸다.그래서 부랴부랴 C++에서 사용했던 맵을 다시 사용했다. const users: User[] = await this.userRepo.find();// new Mapconst userMap: Map = new Map(users.map((u) => [u.id, u]));// hasif(userMap.has(1)) console.log('id 1인 유저 포함');// getconst user1 = userMap.get(1);// setuserMap.set(2,{..

Server/성능개선 2025.12.10

평균 15s API 응답속도 2s로 줄이기

문제상황모든 구매목록을 가져오는 API가 있다.총 구매목록은 2만여건이지만, 조인된 테이블들의 데이터 가짓수를 합치면 60만여건이 넘었다.DTO를 활용하여 모든 테이블을 조인하고 테이블별 검색조건을 where절로 한 번에 검색할 수 있는 편리함이 있었지만, 검색을 하지 않아도 되는 상태에서도 모든 테이블을 조인하는 문제가 있었다.그래서 해당 페이지에 진입하면 모든 구매목록을 가져오기 때문에 15초라는 긴 시간이 소요됐다. 해결방법리스트 페이지는 디테일 정보를 표기할 필요가 없으므로, 필요없는 테이블은 조인에서 제외한다.DTO에 따라 검색조건이 들어오면, 필요한 테이블만 조인한다.인덱스를 탈 수 없는 LIKE 검색은 최소화한다.검색조건으로 사용되는 컬럼들은 인덱싱을 설정한다. 사이드 이펙트해당 API에서 ..

Server/성능개선 2025.10.17

MySQL, TypeORM) Lock으로 동시성 문제 해결

전제조건product 테이블에는 stock이라는 재고가 있다.재고는 0이 되면 구매할 수 없다.a, b 유저가 동시에 동일한 상품을 구매한다. 로직 a, b유저는 재고가 존재하는 상품의 정보를 불러온다.SELECT * FROM product WHERE id = 1 AND stock > 0; 문제 상황a가 먼저 상품을 구매하고 재고를 차감하여 재고가 0이 되면, b는 불러왔던 상품을 구매할 수 없다.트랜잭션을 사용했지만 두 유저가 동시에 테이블에 접근하는건 막을 수 없었다. 해결 방법상품을 읽을 때 부터 Lock을 걸어 해당 행을 다른 유저가 가져올 수 없게 만든다.Lock을 건 상황에서 에러가 발생했다면, 빠르게 락을 해제해야한다. 무한히 기다리는 데드락이 발생할 수 있기 때문.SELECT * FROM ..

Server/성능개선 2025.10.17

NestJS) AWS-S3 효율적인 업로드 방법

배경회원가입시 이미지를 s3에 업로드한다.방법 1프론트에서 이미지를 인풋태그에 삽입하면, 블롭으로 이미지를 만들어 미리보기를 만든다.서버에 formData로 바로 전송해 하나의 API에서 검증 및 S3업로드, 다른 로직을 수행한다. 장점하나의 API에서 작업하니 편리하다 단점바디를 formData로 받아야한다.스웨거 작성 등 직접 나열해야 하는 이유로 불편하다.방법 2프론트에서 이미지를 인풋태그에 삽입 시, API를 한 번 호출한다.API에서는 getSignedUrlPromise 함수로 만료시간이 존재하는 임시 이미지를 버킷에 생성한다.해당 url과 파일 정보를 받아 프론트에서 이미지 태그로 미리보기를 만든다.서버에 json 바디로 파일정보를 가입할 회원의 정보와 함께 송신한다.서버에서는 회원 검증을 마..

Server/성능개선 2025.08.07

NestJS) 의존성주입에서 객체 초기화 문제

먼저 백그라운드를 정해본다. 1. AppModule에 임포트 순서대로 모듈의 초기화가 진행된다.import { Module } from '@nestjs/common';import { TypeOrmModule } from '@nestjs/typeorm';import { AppController } from './app.controller';import { AppService } from './app.service';import { EnvConfigModule, TypeOrmConfigService,} from './config';import { AuthModule } from './modules/auth/auth.module';import { AdminModule } from './modules/adm..

Mac) MySQL 8.4 native_password 사용

AWS에서 MySQL 8.0을 업그레이드하라고 한다.다음 버전인 8.4는 아래와 같은 auth 규칙을 따른다.https://dev.mysql.com/doc/refman/8.4/en/native-pluggable-authentication.html MySQL :: MySQL 8.4 Reference Manual :: 8.4.1.1 Native Pluggable Authentication8.4.1.1 Native Pluggable Authentication MySQL includes a mysql_native_password plugin that implements native authentication; that is, authentication based on the password hashing met..

Server/DB 2025.07.31

NestJS) try-catch Error handling with TS

기본적인 트라이 캐치문 사용이다.try { } catch (error) { } TS에서 에러 타입지정을 하면 에러가 발생한다.try { } catch (error: Error) { } interface Error로 사용하려 했지만,캐치문에서 네스트 프레임워크는 에러를 unknown으로 지정하라고 강요한다.항상 에러가 아닌, 특정 객체가 올 수 있기 때문.ex) throw "error string";throw 123;throw {};throw new Error(); 그래서 개발자가 캐치문에서 에러를 에러로 강제 지정하고 메세지를 출력하려면 아래와 같이 사용해야 한다.try { } catch (error) { console.error((error as Error..

Discord Bot 무료배포

간단하게 슬래시커맨드를 사용할 수 있는 디스코드 봇을 만들었다.https://github.com/Juzdalua/JuniWorld_bot GitHub - Juzdalua/JuniWorld_bot: Create discord bot with my friendsCreate discord bot with my friends. Contribute to Juzdalua/JuniWorld_bot development by creating an account on GitHub.github.com Vercel은 서버리스로 NestJS를 배포할 수 있다.응답이 없으면 서버가 죽는게 서버리스인데, 디스코드 봇은 client.login처럼 소켓 방식으로 봇이 길드 채널에 입장하고 수신을 대기한다. 서버리스에서는 봇이 입장 ..

Server/Infta 2025.06.16

싱글스레드에서 비동기 함수의 이해

node.js, NestJS를 처음 시작하고 await을 사용한 비동기는 펜딩 결과를 기다려주는 것이라고 이해했다.await을 사용하지 않고 결과값을 출력해보면 Pending만 남았었기 때문이다.async/await 비동기 함수는 pending이 아닌 결과값을 기다리기 위한 수단이라고 이해하고 있었다. C++을 1년 넘게 사용하면서, 멀티스레드 환경에서 비동기 방식을 다룰 때 혼동이 있었다.TCP 통신에 대한 간단한 로직을 살펴보자. 스레드를 1개만 사용할 경우,메인스레드 A는 데이터를 수신하기 위해 recv함수에 진입하고 결과값을 기다린다.이 상태는 시스템이 멈춘 블락킹 상태이다.recv함수에서 결과값을 받은 A 스레드는 다음 로직을 수행한다. 스레드를 2개 사용할 경우,메인스레드 A는 데이터를 수신하..