TIL/DB

[DB] 외래키를 사용해? 말어?

JoJobum 2022. 7. 16.

이번 프로젝트에서 DB를 설계하고 구현하면서 외래키를 사용하지 않지만 외래키의 역할을 하는 컬럼을 만들어서 테이블을 구성하였는데, 이에 대한 고민들 적어보고자 한다.

 

서비스에서 여러 소셜 로그인을 지원하고자 하는데 

같은 유저 A가 있을 때 나는 유저의 특정 정보는 유저 엔티티에 두고 

카카오 로그인을 사용한다면 카카오 유저를 만들어 유저와 연결하고

구글 로그인을 사용한다면 구글 유저를 만들어 유저와 연결하는 방식으로 DB를 구성하고자 했다. 

그 중 유저와 카카오 유저의 케이스를 들고 와서 처음 말했던 외래키에 대한 이야기를 하고자 한다.

// 유저 엔티티

@Entity()
export class User {
  @PrimaryGeneratedColumn() 
  id: number;

  @Column({
    nullable: true,
  })
  @Index()
  familyId: number;

  @Column({
    nullable: true,
  })
  nickname: string;

  @Column({
    nullable: true,
  })
  gender: string;

  @OneToOne(() => LocalUser, (localUser) => localUser.user, {
    nullable: true,
  })
  localUser: LocalUser;

  @OneToOne(() => KakaoUser, (kakaoUser) => kakaoUser.user, {
    nullable: true,
  })
  kakaoUser: KakaoUser;
}
//카카오 유저 엔티티

@Entity()
export class KakaoUser {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ type: 'bigint' })
  @Index()
  kakaoId: string;

  @Column({ nullable: true })
  accessToken: string;

  @Column({ nullable: true })
  refreshToken: string;

  @Column({ nullable: true })
  @Index()
  userId: number;

  @OneToOne(() => User, (user) => user.kakaoUser, {
    createForeignKeyConstraints: false,
  })
  @JoinColumn({ name: 'user_id' })
  user: User;
}

이렇게 구성하였는데 이러면 

이제 유저와 카카오유저가 @OneToOne 데코레이터를 통해 일대일 관계를 갖게된다. 그리고  카카오 유저의 @OneToOne()의 옵션에 외래키를 자동생성하는 값을 false로 하여 끔으로서 외래키를 생성하는 것을 막고 대신 외래키의 역할을 해줄 Column인 userId를 만들었다.

 

이제 나중에  A 라는 유저의 유저 정보와 카카오 유저로서의 데이터가 필요하다면 외래키가 아닌 userId 컬럼을 외래키처럼 사용하여 Join 하여 활용한다. 

 

이렇게 하는 부분을 팀원에게 들었을 때 왜 이렇게 하냐?? 의문이 들었는데, 그 이유는 

외래키를 사용하면 cascade 등 외래키를 사용할 때 지켜야 하는 부분들을 지키면서 개발을 하는 것이 불편한다. 

외래키를 사용하면 위의 조건들을 검사해야하기 때문에 성능이 느려진다. 

라는 이유였다.

 

사실 데이터베이스 강의를 들었을 땐 외래키가 테이블간의 관계를 만들어주고, 필수적인, 중요한 느낌을 받았었다. 애초에 외래키를 사용함으로서 얻을 수 있는 장점 중 하나인 참조 무결성 (Referential Integrity)이라는 말이 너무 폭력적이지 아니한가? 그냥 지켜줘야할 거같은 보호 본능이 드는 단어이다.

 

그래서 참조 무결성이 뭐냐면 원래 유저의 id 가 10이고 카카오 유저의 user에 대한 외래키 값이 10이라 이 키값을 통해 참조를 하는데 한쪽의 키값이 20으로 변한다면 다른 쪽도 20으로 자동적으로 수정함(cascade) 으로서 참조하는데 문제가 없게 만들어 준다 == 참조 무결성 을 유지

반면 위와 같은 경우는 유저의 id가 갑자기 20으로 변경된다면 기존에 id 10에 연결되어 있던 카카오 유저가 유저 데이터를 참조하지 못하고 문제가 발생할 것이다.

 

그래서 어떻게 보면 외래키를 사용하냐 하지 않냐는 DB가 규칙을 강요하냐 vs 규칙없이 알아서 잘 설계하기 로 볼 수도 있다.                                                                                                                                    

하지만 위와 같이 외래키를 사용하지 않는 경우가 실제 현업에서 흔한 일이고 궁금해서 리서치를 할수록 이에 대한 갑론을박이 많다. 

 

보통 사용하지 않는 쪽에서의 이야기는

1. 성능

2. 레거시 데이터들을 굳이 지우지 않아도 되는 경우 혹은 지우면 안되는 경우

3. 개발할때 불편하다 (데이터 구조가 방대하고 양이 많아질수록)

 

구체적인 예시

참조되는 릴레이션은 참조하는 릴레이션보다 먼저 만들어져 있어야 함. 때문에 생성, 수정할 때마다 순서를 신경 써줘야 하는 번거로움이 발생하게 된다

CASCADE로 설정해두었을 경우에 한 릴레이션의 데이터를 실수로 바꿨을 경우 참조하는 릴레이션들의 데이터가 모두 변경됨 하지만 이전 데이터를 보존해야 하는 경우가 있을 수 있음

ON DELETE 나 ON UPDATE 옵션이 제대로 기능하려면 매번 무결성을 검사해줘야 하니 속도, 성능 저하가 발생할 수 있다는 얘기도 있다

 

 

결론을 내리자면 외래키를 사용하는 것이 이상적인 설계이지만, 현실적으로  여러 불편함이 있으니 상황에 맞게 사용 여부를 정하여 사용하면 좋을 것 같다. 

 

 

참고 문서

Foreign Key 없이 구축하는 관계형 데이터베이스 시스템에 대한 생각 | SK(주) C&C’s TECH BLOG (engineering-skcc.github.io)

 

Foreign Key 없이 구축하는 관계형 데이터베이스 시스템에 대한 생각

Foreign Key (Referential Integrity) 없이 구축하는 관계형 데이터베이스 시스템에 대한 생각

engineering-skcc.github.io

[DB] 외래키의 장점과 단점 및 논의 (velog.io)

 

[DB] 외래키의 장점과 단점 및 논의

데이터베이스 개론을 들으면, 외래키는 테이블과 테이블간의 연관관계를 맺어주는역할을 한다고 한다. 생각해보면 외래키의 여러가지 장점만 배우고,예를들어 제약조건 생성, 테이블관의 명시

velog.io

 

9 reasons why there are no foreign keys constraints in your database (referential integrity checks) - Dataedo Blog

 

9 reasons why there are no foreign keys constraints in your database (referential integrity checks) - Dataedo Blog

 

dataedo.com

[mysql] foreign key, 외래키 설정은 꼭 해야 하는가? (tistory.com)

 

[mysql] foreign key, 외래키 설정은 꼭 해야 하는가?

이전에 외래키를 설정하지 않고 테이블들을 관리하던 프로젝트에 참여했던 적이 있었다. 당시에는 별 생각이 없었는데, 다시 보니 이렇게 해서 얻는 이점이 있었을까? Insert 과정에서 빡빡하게

jaehaaheaj.tistory.com

외래키를 사용하지 않는 이유 feat. 인덱스 (tistory.com)

 

외래키를 사용하지 않는 이유 feat. 인덱스

외래 키(foreign key) 란 관계형 데이터베이스에서 다른 릴레이션(테이블)을 참조할 때 사용하는 키입니다. 아래 학생 릴레이션을 봅시다. id 학생명 강의명 강의코드 1 개똥이 병충해 방지기법 1231 2

co1nam.tistory.com

 

반응형

'TIL > DB' 카테고리의 다른 글

[Oracle] APPEND 힌트와 Direct Load Insert  (1) 2024.02.14
[DB] soft delete vs hard delete (feat. isDeleted)  (2) 2022.09.05
[DB] Delete vs Truncate vs Drop  (0) 2022.08.17
[DB] Postgres 명령어  (0) 2022.08.17

댓글