Spring으로 프로젝트를 진행하던 도중, 한가지 벽에 가로막혔다.
회원가입에는 두가지 종류가 있다. Social 회원가입과, 그렇지 않은 회원가입, 두가지이다. 나는 그 두가지 회원가입의 로직과 필요한 field값도 다르므로, Table을 따로 두기로 했다.
따라서 SocialMember Entity와 NonSocialMember Entity가 필요하다.두가지 Entity는 공통적인 부분이 존재한다. Member라는 부모 Entity 클래스를 두고, 두가지 Entity가 Member Entity를 상속받게 하였다.
하지만 DB에 접근하기 위한 Repository 인터페이스를 선언하려고 하는데, 이때, Member의 Type을 선언하면 결국 인터페이스를 쓰는 문제가 생기지 않는다는 문제를 발견했다. 그렇다고, Member 의 Type이 아닌 NonSocialMember Entity를 Type으로 설정을 한다면, interface의 존재는 의미가 없었다.
public interface MemberRepository {
Member save(Member member); //상속받은 NonSocialDtoMember를 참조해야함.....
//jdbc template 이용 예정이므로 jdbc template insert 방법을 익힐것. https://www.springcloud.io/post/2022-06/jdbctemplate-id/#gsc.tab=0
Optional<Member> findByEmail(String Email);
Optional<Member> findByName(String Name);
}
이것이 문제였다.
이걸 당장에,
public interface MemberRepository {
Member save(NonSocialMember member); //Dto_Member가 아니라 상속받은 NonSocialDtoMember를 참조해야함.....
//jdbc template 이용 예정이므로 jdbc template insert 방법을 익힐것. https://www.springcloud.io/post/2022-06/jdbctemplate-id/#gsc.tab=0
Optional<NonSocialMember> findByEmail(String Email);
Optional<NonSocialMember> findByName(String Name);
}
이렇게 바꾸면, 결국 구현체를 interface 한 것과 다름 없으므로, 쓸 이유가 없었다.
하지만 Java에서 지원하는 Generic Type을 적극 활용하면 이를 해결할 수 있다.
[공식문서] https://docs.oracle.com/javase/tutorial/java/generics/types.html
public class Box {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}
다음의 코드를 보면, Object라는 Data Type은 String이 매개변수로 오든, Integer가 매개변수로 오든 상관없이 error가 발생하지 않는다. 하지만 여기서 Box 클래스를 이용해서 다른 작업을 할 경우에, Data Type이 지정되지 않았기 때문에 많은 오류를 만나게 된다. 그래서, Data Type은 반드시 지정을 해주어야 한다.
따라서 컴파일시 강한 타입 체크를 하기 위해, 그리고 또 중복되는 코드이지만 타입을 다양하게 주기 위해서는 Generic Type이 필요해진다.
/**
* Generic version of the Box class.
* @param <T> the type of the value being boxed
*/
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
Box<T> 에서 T는 Type Parameter라고 부른다.
T말고 다른 영어를 사용해도 되는데, 나름의 Naming Convention이 있으니, 아까 태그한 공식문서를 보면 된다.
Parameter는 Foo<T> 의 T가 Parameter이고, Foo<String> 의 String이 Argument이다.
public class NonSocialMemberRepository implements MemberRepository<NonSocialMember>{
private final JdbcTemplate jdbcTemplate;
public NonSocialMemberRepository(DataSource dataSource){ //IDE에서 버그로 dataSource 빨간줄쳐지기도 함
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
//DAO 의 Member 객체로 정의
public NonSocialMember save(NonSocialMember member) {
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(this.jdbcTemplate);
//----------------- member_user 테이블 insert ----------------//
jdbcInsert.withSchemaName("MEMBER").withTableName("USER").usingGeneratedKeyColumns("user_id");
System.out.println("real");
Map<String, Object> userTable = new HashMap<>(3);
userTable.put("user_name", member.getUser_name());
userTable.put("login_type", 0);
LocalDateTime currentDateTime = LocalDateTime.now();
userTable.put("user_createdAt",currentDateTime);
Number user_key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(userTable));
member.setUser_id(user_key.longValue());
//----------------------------------------------------------//
//---------------- auth_nonsocial 테이블 insert -------------//
jdbcInsert.withSchemaName("MEMBER").withTableName("auth_nonsocial").usingGeneratedKeyColumns("user_nonsocial_id");
Map<String,Object> authTable = new HashMap<>(3);
authTable.put("user_email", member.getUser_email());
authTable.put("user_pwd",member.getUser_pwd());
authTable.put("user_id",user_key);
//----------------- auth_nonsocial 테이블 insert -------------//
Number nonsocial_key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(authTable));
return member;
}
@Override
public Optional<NonSocialMember> findByEmail(String Email) {
return Optional.empty();
}
@Override
public Optional<NonSocialMember> findByName(String Name) {
return Optional.empty();
}
private RowMapper<NonSocialMember> memberRowMapper(){
return new RowMapper<NonSocialMember>() {
@Override
public NonSocialMember mapRow(ResultSet rs, int rowNum) throws SQLException {
NonSocialMember member = new NonSocialMember();
member.setUser_id(rs.getLong("user_id"));
member.setUser_name(rs.getString("user_name"));
return member;
}
};
}
}
맨 첫번째 줄을 잘 보자. `<NonSocialMember> 가 반드시 구현체 정의부분에 존재해야 한다.