논리적 모델링 단계에서는 개념적 모델링에서 그렸던 ERD의 관계선을 테이블과 외래 키(Foreign Key) 로 구체화한다. 이전 단계에서 다양한 키(Key) 로 데이터의 유일성을 어떻게 보장할지 고민했다면, 이제 그 키들을 이용해서 테이블이라는 섬들 사이에 다리를 놓는 과정을 진행한다.
이때 핵심은 다음 두 가지이다.
객체 지향 프로그래밍(OOP)에 익숙한 개발자는 보통 "관계에는 방향이 있다"고 생각하기 쉽다. 예를 들어 다음과 같은 객체를 떠올린다.
class Team {
Long teamId;
String name;
List<Member> members = new ArrayList<>(); // 팀이 회원들을 참조
}
class Member {
Long memberId;
String name;
// 팀에 대한 참조가 없다고 가정
}
Team → Member 로는 members 리스트를 통해 쉽게 접근한다.Member → Team 으로는 접근할 수 없다. Member 안에 Team 참조가 없기 때문이다.이것이 방향이 있는 단방향 관계이다. 반대로 Member 안에도 Team team 필드를 추가하면 양방향 관계가 된다.
반면 관계형 데이터베이스의 관계는 방향이 없다.
member.team_id 외래 키 하나만 있으면,member 를 기준으로 team 을 조회해도 되고,team 을 기준으로 member 를 조회해도 된다.즉, FK는 그저 두 테이블을 연결하는 제약조건일 뿐, 조회 방향을 제한하지 않는다.
관계를 이해하기 위해 가장 흔한 예제인 팀(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 를 참조하는 외래 키이다. 이제 이 외래 키 하나만으로 양방향 조회가 모두 가능해진다.