기존에는 SQL문을 익히고, DB와 오고가는 과정을 확인하기 위해 ORM 없이 DB에 접근했다. 이번에 서비스를 하나 만들어보면서 경험을 위해 TypeORM으로 마이그레이션을 해보았다. 구글링하면서 배우면서 가장 많이 본 내용은 버전에 관련한 내용이였다. typeORM이 업데이트를 하면서 deprecate된 것이 워낙 많아서 기존 그대로 사용하는 유저들이 많다고 한다.
내가 포스팅을 남기고자 하는 이유는 분명 많은 유저들이 있을텐데.. 업데이트된 버전을 사용하는 사람들은 정말 대부분이 Nest.js를 사용하는 것 같았다. typeORM이 업데이트된 내용 중에 가장 대두되는 부분이 repository를 세팅하는 부분이였다.
아래 포스팅을 통해 업데이트하면서 바뀐 이유를 알고 접근하는데 도움이 되었다.
https://velog.io/@kisuk623/TypeORM%EC%9D%80-%EC%99%9C-Custom-Repository%EB%A5%BC-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94%ED%96%88%EC%9D%84%EA%B9%8C
TypeORM은 왜 Custom Repository를 비활성화했을까?
TypeORM은 nodeJs환경에서 사용할 수 있는 ORM 중 하나다. TypeORM을 제외하고 많이 사용되는 ORM에는 Sequelize, Prisma가 있다.
velog.io
아무튼, 골뱅이부터 익숙치 않았지만 아무리 구글링을 해도, 지피티에게 물어보아도 몇시간 동안 찾지 못하고 우여곡절 끝에 세팅해낸 샘플을 오픈하고자 한다.
0. 들어가기 전에
기본적으로 typeORM을 사용하면서 가장 많이 접하는 용어는 entity와 repository일 것이다. DB와 맞닿는 부분이라고 보면 된다. 그 중 엔티티는 db의 구조를 그리고, 그 구조대로 repository에 대입해서 db에 넣는 것이다.
mongoDB에 익숙한 분들은 entity를 보면 감이 바로 올 것이다. 스키마인데, DB의 테이블, 컬럼과 정확히 일치하도록 세팅해야 한다. db에서 먼저 조건을 설정해놨다가, 엔티티를 세팅하는 부분에서 애를 먹어서 그냥 쉽게 세팅해놓고 하나씩 바꾸면 편할 것 같다.
디렉토리 구조는 기존에 mvc에 맞게 세팅해놓아서 models 안에 model들이 있었는데, model을 나누어 entity와 repository로 구분했고, name.entity.ts / name.repository.ts 로 하는 것이 관례로 보여 일단 이렇게 네이밍을 지었다. 추후 네이밍컨벤션과 다른 부분에 대해 확인해보아야겠다.
0-1. 초기 세팅
공식문서를 참고하여 가급적 똑같이 세팅했다. https://typeorm.io/
TypeORM - Amazing ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server,
typeorm.io
export const AppDataSource = new DataSource({
type: "mysql",
host: DB_HOST,
port: 3306,
username: DB_USER,
password: DB_PASSWORD,
database: DB_NAME,
synchronize: DB_SYNCRONIZE,
logging: false,
// entities: [AuthEntity],
// entities: [__dirname + "/../models/entities/*.entity.{js,ts}"],
entities: [
"/Users/seunghwankim/myproject/intro-me/intro-me/jamTrack/src/models/entities/auth.entity.ts",
],
migrations: [],
subscribers: [],
});
console.log(AppDataSource.options);
DB 세팅할 때 import { DataSource } from "typeorm"; 으로 DataSource를 불러와서 mysql 세팅하듯 생성하면 된다. 내가 설명하는 것 보다는 공식문서에 quick start 항목을 참조하면 쉽게 세팅할 수 있을 것 같다. 여기서 중요한 부분은 entities인데, 나중에 테스트하면서 no metadata 어쩌구 하는 에러내용은 대부분 이 entities의 경로문제인 것 같다. 환경변수로 세팅해놓아서 아래처럼 옵션을 찾으면 입력한 값을 확인할 수 있다. 어떤 에러인지 확실하지가 않아서 일단 하드코딩해놓은 상태다..ㅜㅜ
참고로, syncronize가 true일 때 서버를 시작할 때마다 DB를 초기화하기 때문에 잘 선택해야 한다.
1. Entity
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity({ name: "user" })
export class AuthEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ nullable: true })
username: string;
@Column({ nullable: true })
email: string;
@Column({ nullable: true })
password: string = "";
@Column({ default: "ORIGIN" })
oauth_provide: string;
@Column({ nullable: true })
role?: string;
@Column({ nullable: true })
accessToken?: string;
@Column({ default: 1 })
activated?: number;
constructor(
id: number,
username: string,
password: string,
email: string,
oauth_provide: string
) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.oauth_provide = oauth_provide;
}
}
객체 안에 컬럼마다 인덱스를 설정할 수 있고, typeORM이 에러내용이 모호한 것들이 있었는데, 초반에 세팅하면서 이유를 찾지 못해서 애먹다가 일단 쉽게 설정해놓고 db에 들어가면 그 이후에 차근차근 조건을 설정해가면 좋을 것 같아서 쉽게 해놓았는데, 이렇게 해놓고 하나씩 바꾸면 좋겠다.
지극히 기본적인 부분만 세팅되어있고, typeORM에서 제공하는 기능들을 임포트하면 컬럼에 대한 인덱스를 설정할 수 있으니 필요한 기능은 구글링해보면 좋을 것 같다.
2. Repository
const userRepository = dataSource.getRepository(User).extend({})
여기서 시간을 많이 끌었다. 기본 구조는 sql문 써서 db에 넣을 때랑 크게 다르지 않아서 쉽게 봤다가, 업데이트되어 바뀌어서 자료들도 헷갈리는데 업데이트된 내용에 대한 설명은 죄다 nest.js를 사용하는 것들이라 파헤치는데 오래걸렸다.
레퍼런스를 찾았는데, 여기서 dataSource는 가장 위에 db세팅한 그 함수를 입력하면 된다. 나의 경우엔 AppDataSource이다.
두 번째에 보이는 getRepository의 인자값인 User 자리에는 무엇을 넣어야 할까? repository를 가져오는 것이라지만, 이미 우리는 repository를 세팅하고 있다. 자기 자신을 가져오는 것은 아닐 것이고.. 답은 entity다.
db와 접근할 엔티티를 가져와서 (getRepository). db에 물어본다(dataSource)는 의미로 생각하면 되겠다.
extend..확장.. 그래서 뭐..? ㅎㅎ 여기는 CRUD로 어떤 행동을 할 것인지 입력하는 것이다.
다음은 내가 세팅한 repository의 초기 세팅이다.
import { User } from "../../types/type";
import { AuthEntity } from "../entities/auth.entity";
import { AppDataSource } from "../../loaders/dbLoader";
export const AuthRepository1 = AppDataSource.getRepository(AuthEntity).extend({
async createUser(user: User) {
const { username, email, password } = user;
const userData = this.create({
username,
email,
password,
});
console.log(userData);
await this.save(userData);
return userData;
},
});
이 둘만 알면 기본중의 기본은 바로 챙기는 것 같다. 이후에 또 중요한 것들을 알아가는게 있다면 다음 포스팅에서 이어가보겠다.
'개발 > DB' 카테고리의 다른 글
[sequelize] 서비스 - 모델 로직 분리, 리팩토링 과정 (0) | 2023.10.13 |
---|---|
[sql] 다른 schema 간 DB table 복사방법 (0) | 2023.08.26 |
[mySQL] root 비밀번호 오류 초기화 간단설명, 초기화해도 안되는 경우 (0) | 2023.07.05 |
[오류 해결]Error: Bind parameters must not contain undefined. To pass SQL NULL specify JS null (0) | 2023.06.14 |
[MySQL] 내가 보기 위해 만든 SQL CRUD 구문 정리 (0) | 2023.05.29 |