1. 쿼리 빌더(Query Builder)
- 정의: 쿼리 빌더는 SQL 쿼리를 직접 작성하지 않고도 데이터베이스 쿼리를 생성할 수 있는 도구이다.
- 사용 이유: SQL 표현식을 문자열로 작성하여 데이터베이스에서 직접 계산을 수행하도록 한다. 이는 성능을 최적화하고, 복잡한 쿼리를 유연하게 생성할 수 있게 한다.
- 예시: set({ columnPosition: () => "columnPosition + 1" })는 columnPosition을 1씩 증가시키는 SQL 표현식을 설정한다.

- ** 여기서 신기한 점은 바로 문자열( " " ) 을 사용해서 하는것인데 이건 쿼리 빌더가 SQL 쿼리를 생성할 때 해당 문자열을 그대로 SQL의 일부로 포함시키기 때문이다. 앞으로도 유용하게 쓰일 것 같으니 자주 이용해서 익숙해져 보자.
2. 구조 분해 할당(Destructuring Assignment)
- 정의: 객체나 배열에서 특정 값을 추출하여 변수에 할당하는 JavaScript 문법이다.
- 사용 이유: 코드의 가독성을 높이고, 필요한 데이터만 쉽게 추출할 수 있다.
- 예시: const { columnPosition: newPosition } = updateColumnDto;는 updateColumnDto 객체에서 columnPosition을 추출하여 newPosition 변수에 할당한다.

3. 비동기 함수와 Promise
- 정의: 비동기 작업을 수행하고, 나중에 결과를 반환하는 함수이다.
- 사용 이유: 비동기 작업의 결과를 처리할 때, Promise를 사용하여 코드의 흐름을 관리할 수 있다.
- 예시: async function getMaxColumnPosition(boardId: number): Promise<number> { ... }는 비동기적으로 최대 컬럼 위치를 조회하고, Promise로 결과를 반환한다.
** 여기서 Promise<number>
여기까지가 오늘 한 요약본이고
오늘 나를 되게 힘들게 만들었던건 순서변경 로직이다.
코드로 보게 된다면 이렇게 완성이 되는데 이게 참 복잡하다.
async update(id: number, updateColumnDto: UpdateColumnDto): Promise<string> { // 컬럼 업데이트 메서드
const column = await this.columnRepository.findOne({ where: { id } }); // 리포지토리 인스턴스를 사용해서 아이디를 조건으로 특정 컬럼 데이터를 조회
if (!column) {
throw new NotFoundException('컬럼이 존재하지 않습니다.'); // 컬럼이 존재하지 않을 경우 오류 발생
}
// 업데이트 DTO 에서 컬럼포지션 속성을 추출하여 뉴포지션 이라는 새로운 변수에 할당함.(구조분해할당)
const { columnPosition: newPosition } = updateColumnDto;
const nowCurrentPosition = column.columnPosition; // 현재 컬럼 포지션을 현재포지션 이라는 새로운 변수에 할당함.(구조분해할당)
if (newPosition !== nowCurrentPosition) {
// 이동 방향에 따라 다른 컬럼들의 위치를 조정
if (newPosition > nowCurrentPosition) {
// 현재 위치보다 큰 위치의 컬럼들을 하나씩 앞으로 이동
await this.columnRepository // 리포지토리 인스턴스를 사용해서 쿼리 빌더를 생성
.createQueryBuilder() // 쿼리 빌더를 사용해서 쿼리를 작성
.update(ColumnEntity) // 컬럼 엔티티를 업데이트
.set({ columnPosition: () => "columnPosition - 1" }) // 컬럼 포지션을 앞으로 이동
.where("columnPosition > :nowCurrentPosition AND columnPosition <= :newPosition", { nowCurrentPosition, newPosition }) // 현재 위치보다 크고 새로운 위치보다 작거나 같은 컬럼들을 조회
.execute(); // 쿼리빌더를 사용해서 쿼리 실행
} else {
// 현재 위치보다 작은 위치의 컬럼들을 하나씩 뒤로 이동
await this.columnRepository // 리포지토리 인스턴스를 사용해서 쿼리 빌더를 생성
.createQueryBuilder() // 쿼리 빌더를 사용해서 쿼리를 작성
.update(ColumnEntity) // 컬럼 엔티티를 업데이트
.set({ columnPosition: () => "columnPosition + 1" }) // 컬럼 포지션을 뒤로 이동
.where("columnPosition < :nowCurrentPosition AND columnPosition >= :newPosition", { nowCurrentPosition, newPosition }) // 현재 위치보다 작고 새로운 위치보다 크거나 같은 컬럼들을 조회
.execute(); // 쿼리빌더를 사용해서 쿼리 실행
}
}
// 변경된 컬럼의 위치 업데이트
column.columnPosition = newPosition; // 컬럼 포지션을 새로운 포지션으로 업데이트
await this.columnRepository.save(column); // 리포지토리 인스턴스를 사용해서 컬럼 데이터를 저장
return column + `선택한 ${id} 컬럼이 ${newPosition} 위치로 이동 되었습니다.`; // 업데이트된 컬럼 데이터를 반환
}
자 우선 하나하나 풀어서 설명하겠다.
먼저
async update(id: number, updateColumnDto: UpdateColumnDto): Promise<string>
이 코드는 내가 업데이트 메서드를 만들거다. 거기에 필요한 매게변수는 id:number, 하고 updateColumnDto를 받아서 만들것이다. 이런 느낌이다.
const column = await this.columnRepository.findOne({ where: { id } }); // 리포지토리 인스턴스를 사용해서 아이디를 조건으로 특정 컬럼 데이터를 조회
if (!column) {
throw new NotFoundException('컬럼이 존재하지 않습니다.'); // 컬럼이 존재하지 않을 경우 오류 발생
}
이건 컬럼리포지토리를 인스턴스를 만든것을 사용해서 현재 리포지토리 안에 저 아이디를 조건으로 받아서 특정 컬럼 데이터를 조회 할거다 라는 코드다
그 밑은 만약 내가 찾는 컬럼이 없게 된다면 '컬럼이 존재하지 않습니다.' 라는 메세지를 보내면서 오류를 일으키게 되어있다.
// 업데이트 DTO 에서 컬럼포지션 속성을 추출하여 뉴포지션 이라는 새로운 변수에 할당함.(구조분해할당)
const { columnPosition: newPosition } = updateColumnDto;
const nowCurrentPosition = column.columnPosition; // 현재 컬럼 포지션을 현재포지션 이라는 새로운 변수에 할당함.(구조분해할당)
이 작업은 굳이 안해도 되긴 하지만 해두면 좋은게 나중에 모르는 개발자나 아님 티원과의 협업을 할때 가독성이 좋아지게 만들어주는 구조분해할당을 쓴다. 이게 가독성만 좋아지는게 아니라 코드를 작성할때도 편하게 작성 할 수 있어도 좋다.
코드를 한번 살펴보면 updateColumnDto라는 클래스안에서 columnPosition이라는 속성을 추출해서 newPosition라는 새로운 변수에 할당한다.
그 밑은 nowCulumnPosition이라는 새로운 변수에 column.columnPosition 즉 현재 포지션을 변수에 할당한다.
if (newPosition !== nowCurrentPosition) {
// 이동 방향에 따라 다른 컬럼들의 위치를 조정
만약 새롭게 부여받은 포지션이 현재 포지션하고 다르다. 즉
3이라는 포지션을 부여받은 컬럼이 2라는 포지션하고 바꾸고 싶다. 이렇게 되면 어떻게 바꿀래? 이런 느낌인거다.
await this.columnRepository // 리포지토리 인스턴스를 사용해서 쿼리 빌더를 생성
.createQueryBuilder() // 쿼리 빌더를 사용해서 쿼리를 작성
.update(ColumnEntity) // 컬럼 엔티티를 업데이트
.where("columnPosition > :nowCurrentPosition AND columnPosition <= :newPosition", { nowCurrentPosition, newPosition }) // 현재 위치보다 크고 새로운 위치보다 작거나 같은 컬럼들을 조회
.set({ columnPosition: () => "columnPosition - 1" }) // 컬럼 포지션을 앞으로 이동
.execute(); // 쿼리빌더를 사용해서 쿼리 실행
} else {
// 현재 위치보다 작은 위치의 컬럼들을 하나씩 뒤로 이동
await this.columnRepository // 리포지토리 인스턴스를 사용해서 쿼리 빌더를 생성
.createQueryBuilder() // 쿼리 빌더를 사용해서 쿼리를 작성
.update(ColumnEntity) // 컬럼 엔티티를 업데이트
.where("columnPosition < :nowCurrentPosition AND columnPosition >= :newPosition", { nowCurrentPosition, newPosition }) // 현재 위치보다 작고 새로운 위치보다 크거나 같은 컬럼들을 조회
.set({ columnPosition: () => "columnPosition + 1" }) // 컬럼 포지션을 뒤로 이동
.execute(); // 쿼리빌더를 사용해서 쿼리 실행
}
자 이게 뭐하는 코드냐 그건 바로 내가 직접 복잡하게 계산을 안하고 데이터베이스한테 직접 계산을 수행하도록 만드는
TypeORM의 "쿼리 빌더" 라는거다. 이걸 사용하기 위해선
1. 처음에 .createQueryBuiler() 를 사용해서 쿼리를 작성할수 있게 해준다.
2. .update(ColumnEntity) 이건 컬럼 엔티티를 업데이트 시켜주는것이다.
3. .where("columnPosition > :nowCurretPosition AND columnPosition <= : newPosition", { nowCurrentPosition, newPosition}) 이건 현재 위치보다 크고 새로운 위치보다 작거나 같은 컬럼들을 조회하는것이다.
4. .set({ columnPosition: () => " columnPosition -1" 이건 특정 새로 부여받은 컬럼하고 현재 컬럼하고 숫자가 다르게 된다면 다른 columnPosition 즉 다른 컬럼들은 -1을 시켜주는 것이다. 이로써 위치를 앞으로 이동하게 되는것이다.
5. .execute() 는 쿼리빌더를 사용해서 쿼리를 실행하겠다는 뜻이다.
그 밑은 반대로 진행이 된다.
// 변경된 컬럼의 위치 업데이트
column.columnPosition = newPosition; // 컬럼 포지션을 새로운 포지션으로 업데이트
await this.columnRepository.save(column); // 리포지토리 인스턴스를 사용해서 컬럼 데이터를 저장
return column + `선택한 ${id} 컬럼이 ${newPosition} 위치로 이동 되었습니다.`; // 업데이트된 컬럼 데이터를 반환
이건 새롭게 변경된 컬럼을 새로운 포지션으로 이동 업데이트를 시켜준다.
그 다음에는 리포지토리 인스턴스를 사용해서 컬럼 데이터를 저장하게 되면 순서가 변경되는것이다.
마지막으로 컬럼과 자신이 이동이 완벽하게 되었다는 문자를 보내주면 컬럼이동 로직은 된것이다.
우선 오늘 하루는 이렇게 마무리를 짓는데 이번주는 설날이여서 설날동안 조금씩 만지고 금요일에 팀원들과 자신의 api를 합치기로 했다.
오늘 하루도 고생했고 다음 수업때도 힘내자!