
서비스의 유저 타입들은 @Inheritance(strategy = InheritanceType.JOINED)와 @DiscriminatorColumn을 사용하여 구분한다. 이때 필드를 중복 정의하면서 shadowing 문제가 발생했는데, 다음과 같은 현상이 나타났다:
@Getter
@Entity
@Table(name = "users", schema = "")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "dtype", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue("general") // 기본값
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
..
..
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "firm_id", nullable = true) // 소속 Firm
private Firm firm;
}
@Entity
@DiscriminatorValue("layers")
@Table(name = "users_lawyer", schema = "lemon")
public class LawyerUser extends User {
@Column(name = "firm", length = 255)
private String firm; // 중복 정의된 필드
@Column(name = "license_number", length = 100)
private String licenseNumber;
@Column(name = "license_file", length = 255)
private String licenseFile;
@Column(name = "firm_phone", length = 20)
private String firmPhone;
}
@Entity
@DiscriminatorValue("layers")
@Table(name = "users_lawyer", schema = "lemon")
public class LawyerUser extends User {
@Column(name = "license_number", length = 100)
private String licenseNumber;
@Column(name = "license_file", length = 255)
private String licenseFile;
// firm 필드 제거 - 이미 User 클래스에 정의되어 있음
@Column(name = "firm_phone", length = 20)
private String firmPhone;
}
이번 문제는 단순히 코드 수정에 그치지 않고, 설계의 중요성을 다시 돌아보는 계기가 되었다.
처음에는 원인을 찾는 데 어려움을 겪었고, 문제의 본질을 파악하기 위해 디버깅과 가설 검증을 반복하며 꽤 오랜시간이 걸렸다.
JPA 프록시 문제나 설정 오류로 오인하며 여러 단계를 거쳤지만, 결국 중복 정의된 필드가 원인임을 발견했다.
문제 해결 후에는 "생각보다 간단하네"라는 허무함이 들었다. 그러나 이 과정을 통해 설계의 작은 실수가 데이터 접근과 동작에 얼마나 큰 영향을 미칠 수 있는지 실감했다.
Shadowing 문제는 상위 클래스(부모)와 하위 클래스(자식) 간에 같은 이름의 필드나 변수가 정의될 때 발생하는 문제.
하위 클래스에서 같은 이름의 필드를 다시 정의하면, 이 필드가 상위 클래스의 필드를 가리게(덮어씌우게) 된다.
결과적으로, 상위 클래스의 필드에 접근하려고 해도 하위 클래스의 필드가 대신 사용된다.
이 현상은 JPA뿐만 아니라 일반적인 객체 지향 프로그래밍에서도 발생할 수 있으며,
다음과 같은 부작용을 초래한다
해결 방법은 중복 정의를 피하고, 명확한 설계를 통해 하나의 필드만 사용하도록 하는 것