
Spring Data JPA를 사용하다 보면, 1:1 관계에서 외래 키(FK)를 기본 키(PK)로 사용하는 설계가 필요할 때가 있습니다.
예를 들어 프로젝트 K-FreeMarket에서 Question 엔티티가 존재하고, 그에 대한 Answer가 정확히 하나만 존재해야 하는 것입니다.
이번 글에서는 Answer의 기본 키를 Question의 외래 키로 사용하는 방식과,
이때 사용하는 핵심 어노테이션인 @MapsId의 개념 및 작동 방식, 그리고 MySQL ERD 상에서 나타나는 한계를 함께 정리합니다.
Question과 Answer는 1:1 관계입니다.Answer는 반드시 Question에 종속됩니다. 즉, Question이 존재해야 Answer를 저장할 수 있습니다.Answer의 기본 키(PK)는 Question의 외래 키(FK)를 그대로 따릅니다.이 구조를 구현하기 위한 핵심 키워드는 바로 @MapsId입니다.
@MapsId는 JPA에서 외래 키(FK)를 기본 키(PK)로 공유할 때 사용합니다.
쉽게 말해, 연관된 엔티티의 PK를 이 엔티티의 PK로 "덮어쓰도록 매핑"하는 역할을 합니다.
//Question.java
@OneToOne(mappedBy = "question", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Answer answer;
// Answer.java
@Id
@Column(name = "question_id")
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@MapsId // 핵심! 이걸 통해 question_id를 PK로도 사용
@JoinColumn(name = "question_id")
private Question question;
}
@Id 필드는 question_id와 매핑되어 PK로 동작합니다.@JoinColumn(name = "question_id")는 Question의 FK를 설정합니다.@MapsId는 FK 필드인 question을 통해 자동으로 PK 값도 설정되도록 해줍니다.즉, Answer의 id는 명시적으로 설정하지 않아도, question 필드를 통해 자동으로 세팅됩니다.
Question q = questionRepository.save(new Question(...));
Answer a = new Answer();
a.setQuestion(q); // a.setId(q.getId())는 필요 없음
answerRepository.save(a);
MySQL Workbench 등 ERD 도구에서는 Answer → Question의 관계가 1:N처럼 보이는 경우가 많습니다.
이는 ERD 도구가 단순히 외래 키 제약 조건만 보고 기본적으로 1:N 관계로 시각화하기 때문입니다.

그러나 실제 DB 스키마에는 다음과 같이 명시됩니다
CREATE TABLE `answer` (
`question_id` bigint NOT NULL,
`content` varchar(255) DEFAULT NULL,
...
PRIMARY KEY (`question_id`),
CONSTRAINT FOREIGN KEY (`question_id`) REFERENCES `question` (`question_id`)
)
question_id가 PK이자 FK입니다.question_id로는 Answer에 하나의 행만 삽입 가능하므로, 1:1 관계가 성립됩니다.-- 예: 같은 question_id에 두 개의 answer를 삽입해보자
INSERT INTO answer (question_id, content) VALUES (1, '첫 번째 답변');
INSERT INTO answer (question_id, content) VALUES (1, '두 번째 답변'); -
두 번째 쿼리는 PK 중복 오류로 인해 삽입되지 않으며, 이를 통해 1:1 관계가 유지되는 것을 확인할 수 있습니다.

@MapsId는 FK를 PK로 활용할 수 있게 해주는 JPA의 유용한 기능입니다.@MapsId 설계는 매우 유효합니다.[참고]