![복합 인덱스로 쿼리 튜닝하기](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLXSBK%2FbtsIs6kctju%2FxKywLZXmyXPCx48khfbda1%2Fimg.png)
[데이터베이스] 인덱스(index) 정리인덱스 목차, 색인, 책갈피와 같은 기능을 하는 인덱스는, 데이터베이스 분야에서는 어떤 데이터를 검색할 때 속도를 높여주는 자료 구조로메모리 영역에 생성되는 일종의 책갈피이다. mag1c.tistory.com 위 글의 복합 인덱스를 통한 튜닝 부분을 따로 옮긴 포스팅입니다. 분명 일정한 기준이 있을 텐데 왜 얘기들이 조금씩 다른 것일까? DB의 버전 때문일까 옵티마이저가 무조건적으로 100% 맞다는 보장이 없어서일까? 잘 모르겠다. 그래서 직접 쿼리 튜닝의 경험들을 복기하며 복합 인덱스를 생성할 때에는 어떤 순서로 인덱스를 구성해야 하는지 알아보았다. 서비스 내부에는 모든 유저의 장바구니(앞으로 견적함이라 부름)를 볼 수 있는 기능이 존재하는데 업종으로 필터링할 경..
![클라우드 비용을 줄여보자 (AWS 비용 절감 시도 - 1)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9Fjlx%2FbtsGSMsfipL%2FBkGl2G8eeKeyhAPE0A2x5K%2Fimg.png)
프리티어 만료 며칠 전, AWS에서 메일하나가 왔다. 벌써 AWS 프리티어를 사용한지 1년이 다되어가나보다. 아래는, 프리티어에 대한 총 사용량과, 문제의 지지난달 요금이다. 추가로, 로드밸런서의 로깅용으로 S3를 사용했는데 따로 잡히지는 않은 모습이다. 다른 프리티어 사용자분들께 여쭤보고, 포스팅도 찾아보고 해봤지만, 나처럼 요금이 많이 나오는 경우가 잘 없나보다. 만료가 되고서야 한 3~5만원정도 요금이 잡히는 것 같았다. 처음부터 많이 나오지는 않았는데, 과금이 많이 발생한 시점이 사이드 프로젝트를 프론트, 백엔드 모두 배포하여 사용하면서 개발 컨테이너, 프로덕션 컨테이너까지 분리하여 총 네개의 컨테이너를 사용한 시점부터였다. 관련 기술블로그들을 정독해봤지만, 당최 무슨말인지 이해하려면 너무 오래걸..
![[NestJS] Failed to catch error thrown by guard in nestjs in interceptor / guard의 uncaughtException](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FReLfw%2FbtsGwBK2SWb%2FMMvkkOHZbYl5mkChpPdM9K%2Fimg.png)
Guard에서의 uncaughtException 새 프로젝트를 진행중인데, 에러를 캐치하지 못해서 서버가 뻗어버렸다. 바로 본론으로 들어가서, 프로젝트의 에러 핸들링 설계는 아래처럼 구성했었다. 에러 발생 > 인터셉터에서 에러 로깅 및 필요에 따라 WebHook 전송 > 필터에서 클라이언트에 보낼 에러 포맷 정의 이러한 방식의 설계는, NestJS의 요청 응답 사이클과 각 구성요소의 역할에 대해 완전히 이해하지 못했기 때문에 만들어졌다. NestJs req-res lifecycle Interceptor에서 간과한 부분이 있었다. 클라이언트에서 보낸 API 요청이 프로젝트 전역에 설정한 Global Interceptor에서 가로채기 전에 가드에서 에러가 발생했다. 그렇기에 Exception Intercep..
![[Grafana Loki] Data source connected, but no labels received. Verify that Loki and Promtail is configured properly](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbls1Oy%2FbtsFFKvo7Jv%2FD4bDRtyDoHBIb41KvEQmM0%2Fimg.png)
에러 원인 라벨 설정 시 설정했던 라벨이 존재하지 않음. 이는 로키는 제대로 연결되었지만, 로그 파일을 제대로 프롬테일에서 받아오지 못했음을 의미한다. 본인의 경우는 프로젝트의 도커컴포즈 볼륨 설정에서, 경로가 제대로 작성되지 않아 망운트가 제대로 되지 않았음. 걸린 시간 3시간 남짓 에러 해결 프로젝트의 로그생성 docker exec -it containerName /bin/sh 프로젝트 내부에서는, 루트 경로에 logs폴더 내부에서 날짜, 에러레벨에 맞게 분기처리를 하여 로그를 생성했다. 호스트 서버에서, 위 명령어를 통해 컨테이너 내부로 진입하는데, 진입하자마자 logs경로에 로그가 잘 들어오고 있길래 당연히 잘 동작할거라 생각했지만 그라파나에서 로키의 ip:port로 커넥션을 연결하는 과정에서 계..
![[NestJS] 코드 리팩토링하기 - 응집도를 높이고 의존성을 명확하게](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCcOaw%2FbtsDKw7f3o6%2FKwWTpnldsSYUsEndnL4A0K%2Fimg.jpg)
서론 요즘 좋은 코드 라는 키워드에 대해 특히 변경과 재사용이 용이한, 높은 응집도와 낮은 결합 관계 에 대해 많이 생각하고 있다. 특히 기존 레거시를 모두다 걷어내기에는 시간적으로 애로사항이 있어 틈틈이 관련된 프로젝트에 들어갈 때, 해당 로직에 대한 레거시들을 최대한 바꾸려고 노력하고 있다. [네이버클라우드 개발자 스토리] 좋은 코드란 무엇일까?🤔 #클린코드 이야기 📍 “좋은 코드를 짜야 한다” medium.com 특히, 상품의 리뷰를 불러오는 함수를 수정해야 하는 일이 최근에 있었는데, 상품군 7~8개의 하위 상품에 대한 리뷰를 모두 다른 함수에서 불러오는 것을 보고 경악을 금치 못했다. (급한 사항이라 판단되어 우선 프로덕션에 수정해서 반영한 뒤 구조를 수정하였다..) 나도 최근에 신규 프로젝트..
![[NestJS] TypeORM 0.3 버전의 CustomRepository 생성, Repository패턴 적용하기](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWnqSZ%2FbtsC31m5zaC%2FNL1FsWQWiHtM9PMzXn6sDk%2Fimg.png)
0.2 버전 사내 서비스의 TypeORM버전은 0.2버전대를 사용중이다. 0.2버전대에서는 @EntityRepository 데커레이터를 지원하여, Repository를 커스텀화하여 리파지토리 클래스를 생성할 수 있었고, 이에 따라 Service와 Repository레이어를 분리하여 결합도를 낮출 수 있었다. @Injectable() export class RsvcenterService { constructor( @InjectRepository(CustomRsvRepository) private readonly customRsvRepo: CustomRsvRepository, //DB 관련 로직 예외처리 Provider private readonly customEm: CustomEntityManager, )..
![[TypeORM / QueryBuilder] Relation with property path confirms in entity was not found](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1emZ5%2FbtsBDOPlmWN%2FwWYq29fygpbQVQZ4TObVk1%2Fimg.png)
최근 레거시 코드 중 DB 관련 로직들을 거의 대부분 쿼리빌더로 변경하는 작업을 완료하고, 검수중에 있다. 그 과정에서 발생한 에러들을 하나하나 정리하여 남기려고 한다. 에러 메세지 원인 관계 매핑이 정확하지 않아서 발생했다. 나의 경우는 아래 이유 때문에 발생했는데, TypeORM의 쿼리빌더를 사용하는 과정에서, 커스텀 리파지토리를 생성하여, 해당 리파지토리에서 두 테이블을 조인해서 사용했는데, 처음 INNER JOIN을 시도한 테이블에서, enterprise라는 엔터티에 대한 정의를 내리지 않았기 때문에 발생했다. 해당 엔터티를 살펴보면, export class EasyBookWhichEntEntity { @PrimaryGeneratedColumn({ type: 'int', name: 'no' }) ..
![[NestJS] JWT secretOrPrivateKey must have a value](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK0B2K%2FbtszAkxrA6W%2FVRM9v5Y3fF7i7K9GWK7Lp0%2Fimg.png)
에러 메세지 원인 .env에 작성해 둔 JWT_SECRET이라는 이름의 환경변수를 읽는 과정에서 발생한 에러로 JWT 토큰 발행 시 반드시 secret key가 있어야 하는데 읽지를 못한 것으로 보인다. 공식 문서의 JWT 가이드 대로 따라하며 토큰 발급 시 발생했으며, 모듈에서 설정한 secret을 JwtModule에 등록하지 못했다. 환경변수를 읽어오기 전에 secret가 먼저 register되는 것 같아 보였다. @Module({ imports: [ UserModule, JwtModule.register({ global: true, secret: process.env.JWTSecret, signOptions: { expiresIn: '60s' }, }), TypeOrmModule.forFeature..