그 동안 여러 프로젝트를 진행하면서 mongoose
라던가 TypeORM
같은 ORM을 사용 하였다.
이러하다 보니 뭔가 도구를 사용하기 위한 도구를 공부하는 느낌이라,
이참에 ORM을 사용하지 않고 진행하기로 하였다!
MySQL 8버전으로
mysql2
드라이버를 사용하였다.
대부분의 ORM들은 스키마를 정의하면 findOne
이라던가 find
또는 findById
같은 함수를 제공한다.
하지만 mysql2
의 쿼리로 위와 같은 함수의 반환값을 받으려 한다면 어떨가?
SELECT * FROM users WHERE id = 1; // 1
또는
SELECT * FROM posts WHERE user_id = 1; // 2
1번 같은 경우 user.id === 1
인 user 객체 하나가 반환될 것이라 생각 할 것이다.
예상 결과
{
"id": 1,
"email": "test@gmail.com",
}
하지만 안타깝게도 결과는 배열을 내뱉는다.
실제 결과
[
{
"id": 1,
"email": "test@gmail.com",
}
]
LIMIT 1
을 넣어줘도 결과는 배열이다~
답은 생각보다 간단하다. mysql2
라이브러리가 배열만 내놓기 때문이다.
라이브러리를 들여다 보면
// 오버로딩하는 함수 4개 중, 내가 사용하는 하나이다.
query<T extends RowDataPacket[][] | RowDataPacket[] | OkPacket | OkPacket[] | ResultSetHeader>(
sql: string,
values: any | any[] | { [param: string]: any }
): Promise<[T, FieldPacket[]]>;
제네릭 T가 상속받는 것 들을 알 수있는데,
SELECT
같은 경우 RowDataPacket
타입으로 받아 오게 되어있다.
보면은 반환값이 RowDataPacket[][]
또는 RowDataPacket[]
이기 때문에
SELECT
로 1개의 값을 찾아도 배열로 반환 하는 것이다.
안타깝게도 findOne
과 같은 객체 하나만을 반환하는 기능을 구현하기 위해서는
// 반환값의 두 번째 인자는 FieldPacket으로 나에겐 필요 없기 때문에 무시한다.
findUser(id: number) {
const [rows, _] = mysql.query(`SELECT * FROM users WHERE id = ?`, [id]) ;
return rows[0];
}
위 와 같은 방식으로 값을 꺼내 오는 findOne
함수를 따로 구현하거나
ORM
을 사용하여 라이브러리가 제공하는 함수를 사용하는 수 밖에 없다.
class-transformer
의 plainToInstance
을 이용한 객체화.
select
에서 원하는 column을 findUserDto
에 명시 해주면 된다.
//return rows[0]
return plainToInstance(findUserDto, rows[0])// 첫 번째 인자에 변환해주고싶은 Class 주입
ORM 에서는 getOne
과 같은 편의 기능이 제공되나 생 쿼리로만 구현을 하니 이와 같은 부분을 직접 다 구현해야 했다.
SQL 사용에 능숙해져 가는 느낌에 좋았지만 그래도 코드의 관리 힘들기 때문에 ORM을 사용 할 것 같다.
프로젝트를 진행하면서 겪었던 이슈들을 정리하지 않는 나의 게으름을 반성하며 작성하였습니다~