논리적 모델링 단계에서는 개념적 모델링에서 그렸던 ERD의 관계선을 테이블과 외래 키(Foreign Key) 로 구체화한다. 이전 단계에서 다양한 키(Key) 로 데이터의 유일성을 어떻게 보장할지 고민했다면, 이제 그 키들을 이용해서 테이블이라는 섬들 사이에 다리를 놓는 과정을 진행한다.

이때 핵심은 다음 두 가지이다.

1-1. 관계형 DB의 관계는 방향이 없다

객체 지향 프로그래밍(OOP)에 익숙한 개발자는 보통 "관계에는 방향이 있다"고 생각하기 쉽다. 예를 들어 다음과 같은 객체를 떠올린다.

class Team {
    Long teamId;
    String name;
    List<Member> members = new ArrayList<>(); // 팀이 회원들을 참조
}

class Member {
    Long memberId;
    String name;
    // 팀에 대한 참조가 없다고 가정
}

이것이 방향이 있는 단방향 관계이다. 반대로 Member 안에도 Team team 필드를 추가하면 양방향 관계가 된다.

반면 관계형 데이터베이스의 관계는 방향이 없다.

즉, FK는 그저 두 테이블을 연결하는 제약조건일 뿐, 조회 방향을 제한하지 않는다.

1-2. 예제 테이블과 샘플 데이터

관계를 이해하기 위해 가장 흔한 예제인 팀(Team) 과 회원(Member) 관계를 사용한다. 하나의 팀에 여러 회원이 소속되는 전형적인 1:N 관계이다.

DROP TABLE IF EXISTS member_detail; -- 다른 예제에서 발생했을 수 있는 잔여 테이블 제거
DROP TABLE IF EXISTS member;
DROP TABLE IF EXISTS team;

-- 팀 테이블 생성
CREATE TABLE team (
  team_id BIGINT NOT NULL AUTO_INCREMENT,
  name    VARCHAR(50) NOT NULL,
  PRIMARY KEY (team_id)
);

-- 회원 테이블 생성
CREATE TABLE member (
  member_id BIGINT NOT NULL AUTO_INCREMENT,
  team_id   BIGINT NULL, -- 회원은 팀에 소속되지 않을 수도 있다 (NULL 허용)
  name      VARCHAR(50) NOT NULL,
  PRIMARY KEY (member_id),
  CONSTRAINT fk_member_team FOREIGN KEY (team_id)
      REFERENCES team (team_id)
);

-- 샘플 데이터 추가
INSERT INTO team(name) VALUES ('개발팀'), ('기획팀');
INSERT INTO member(name, team_id) VALUES ('션', 1), ('잡스', 1), ('네이트', 2);

이 구조에서 member.team_id 가 team.team_id 를 참조하는 외래 키이다. 이제 이 외래 키 하나만으로 양방향 조회가 모두 가능해진다.

1-3. FK 하나로 양방향 조회하기