개발자꿈나무
FureverHomes 프로젝트 - 1. 기본세팅과 JPA 설정 본문
기본 게시판 스타일의 프로젝트를 하나 해볼까 하다가 예전 팀 프로젝트 당시 나왔었던 주제들 중 괜찮았던 주제가 있었어서 그걸 혼자 작게 구현해보기로 했다.
유기동물 입양과 커뮤니티
원래는 동물병원이나 동물 관련된 기관들의 위치 정보와 후기들을 공유하고 온라인으로 동물의 건강 상태에 대한 질문과 답변을 얻을 수 있는 서비스 혹은 유기 동물에 대한 통계나 위치 정보를 얻어와서 병원과 연계해줄 수 있는 서비스에 대해서 논의를 했었는데, 이 두가지 주제를 어느 정도 가지고 있으면서 기본적인 기술을 보여줄 수 있는 프로젝트를 진행하기로 결심했다. 지도 API를 가지고 와서 유기견 보호소 정보들을 가지고 올 수 있으면 좋겠지만, 이는 버전업 때 해보도록 하고 일단 유기견을 입양하고, 입양 후의 이야기들을 나눌 수 있는 서비스를 만들기로 했다. 이번 프로젝트의 목적은 저번 프로젝트 당시 제대로 핸들링하지 못했던 기술과 새로운 기술을 공부하여 적용해보는 것이다.
먼저, mybatis를 사용했던 저번과 달리 지금까지 열심히 공부해왔던 JPA를 적용해보기로 했다. 그리고 핸들링에 실패해서 사용하지 못했던 Spring Security를 사용해서 완성해볼 예정이다.
UI구성과 로고
UI는 부트스트랩을 이용하여 구현하였고, 노랑, 주황색 계열을 기본 색상으로 잡아서 화면을 구성하였다. 프로젝트 이름인 FUREVER HOMES는 '영원한 집'이라는 뜻에 동물을 상징하는 'Fur'를 가미하여 지었으며, 로고 역시 노랑, 주황색 계열을 이용하여 만들었다. 디자인 감각은 정말 무지하기에 AI의 힘을 빌려서 만들었지만 내가 딱 원했던 느낌을 구성할 수 있어서 굉장히 뿌듯했다.
JPA 엔티티
멤버 동물 관심동물 입양 게시판 댓글 파일 테이블로 총 7개의 테이블로 구성했다. JPA를 공부하면서 정리해뒀던 내용들을 살펴보며 하나하나씩 엔티티를 설정해줬다.
- 멤버 엔티티
@Entity
@Getter
@Setter
@ToString
public class Member {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "MEMBER_ID")
private Long id; //pk
@Column(nullable = false)
private String email; //회원 아이디
@Column(nullable = false)
private String password; //비밀번호
@Column(nullable = false, length = 50)
private String name; //이름
@Enumerated(EnumType.STRING)
@Column(length = 10)
private Sex sex; //성별
@Column(nullable = false)
private LocalDate birth; //생년월일
@Column(nullable = false)
private LocalDate regi_date; //가입날짜
@Column(nullable = false, columnDefinition = "TINYINT(1)")
@ColumnDefault("false")
private Boolean email_auth; //이메일 인증여부
@ManyToMany
@JoinTable(name = "INTEREST_ANIMAL",
joinColumns = @JoinColumn(name = "MEMBER_ID"), inverseJoinColumns = @JoinColumn(name = "ANIMAL_ID"))
private List<Animal> animals = new ArrayList<>(); //연관관계 매핑 - 관심동물 (다대다 단방향일듯)
}
롬복을 이용해서 Getter, Setter, ToString을 만들어줬다. 성별을 EnumClass를 만들어 F, M, N 필드를 만들었다. 동물의 경우에는 암수 식별이 힘들거나 암수 구분이 없는 동물들도 있으므로 N 필드도 생성해줬다. 멤버 엔티티를 생성할 때 고민했던 부분이 이메일 인증여부에서 boolean을 어떤식으로 풀어나갈지였다. 방법들을 찾아보니 @Converter를 이용하여 Y 또는 N으로 처리하는 방법이 많이 보이던데, 나는 그렇게까지 할 필요는 없을 것 같아 tinyint 값을 주고 디폴트를 false로 설정해줬다.
마지막으로 animals 필드는 관심동물들을 나타내는 필드인데, 관심동물과 멤버의 관계는 다대다의 관계이므로 다대일, 일대다로 풀어주는 테이블을 하나 생성해서 관리하도록 만들 생각이다. 엔티티를 굳이 하나 더 만들지는 않았고 @ManyToMany를 이용해서 관리할 생각이다. 회원이 관심동물로 등록해놓은 동물들의 정보는 필요하지만, 동물에 관심동물을 등록해놓은 회원들을 조회하는 경우는 없을 것으로 판단돼서 다대다 단방향으로 매핑해주면 될 것 같다고 생각했다. (이 부분은 추후에 변동이 생기면 다시 정리하도록 하겠다.)
현재 연관관계 매핑은 전부다 단방향으로만 매핑해놓은 상태이다. 본격적으로 구현해나가면서 양방향이 필요하다고 생각하면 그때 매핑을 해줄 생각이다.
- 동물 엔티티
@Entity
@Getter
@ToString
@Setter
public class Animal {
@Id @Column(name = "ANIMAL_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id; //동물 아이디
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Species species; //종
@Enumerated(EnumType.STRING)
@Column(length = 10, nullable = false)
private Sex sex; //성별
@Column(columnDefinition = "TINYINT(1)")
private Boolean nueter; //중성화 여부
@Column(nullable = false)
private String name; //동물 이름
@Lob
private String health_condition; //건강상태
@Column(nullable = false, columnDefinition = "VARCHAR(255) DEFAULT 'default.jpg'")
private String picture; //동물 사진
@Column(nullable = false)
private String shelter_name; //보호소 이름
private String shelter_tel; //보호소 연락처
}
동물의 종을 나타내는 컬럼도 EnumClass를 이용하여 Dog, Cat, Others로 관리할 생각이다. 동물의 중성화 여부를 나타내는 필드는 조금 고민을 했던 부분이 boolean으로 타입을 줄건지 EnumClass를 만들 것인지였다. 입양을 중개해주는 입장에서 중성화 여부를 체크하지 않는다는 부분은 조금 무책임하다고 생각해서 not null로 Y, N만 판단하려고 했는데 강아지나 고양이뿐만 아니라 다른 동물들도 입양을 할 수 있다는 조건이 있으므로 중성화 여부를 체크하기가 힘든 동물이나 암수 구분이 없는 동물들도 있을 수 있다는 생각에 고민을 하기 시작했다. 내린 결론은 굳이 EnumClass를 하나 더 만들어서 '예, 아니요, 모름'으로 구분하는 것보다는 boolean 타입을 주되 null을 허용해서 값이 들어오지 않으면 확인할 수 없는 상태라고 판단하는 것이 낫다고 생각했다.
그리고 jpa를 실행하는 과정에서 오류가 자주 나왔던 부분이 있는데 컬럼의 디폴트 값을 설정해주는 부분이였다. ColumnDefault라는 어노테이션이 존재하는데, 이 역시도 컬럼의 디폴트 값을 설정해주는 기능이라고 생각해서
@Column(nullable = false)
@ColumnDefault("'default.jpg'")
private String picture;
이렇게 디폴트 값을 설정해줬었다. 하지만 실행을 했더니 오류가 생겼고 알아보니 @ColumnDefault는 표준 JPA 명세에는 포함되어 있지 않으며 일부 JPA 구현체에서 사용할 수 있는 기능이라고 한다. 표준 JPA 명세에 포함되어 있는 것은 @Column(columnDefinition = "")이라고 한다.
- 입양 엔티티
@Entity
@Getter
@ToString
@Setter
public class Adopt {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ADOPT_ID")
private Long id; //입양 아이디
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member; //연관관계 매핑 - 멤버
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ANIMAL_ID")
private Animal animal; //연관관계 매핑 - 동물
@Column(nullable = false)
private String phonenum; //입양자 연락처
@Column(nullable = false)
private String contact_time; //연락가능시간
@Column(nullable = false)
private String residence; //거주지
private String job; //직업
@Column(nullable = false, columnDefinition = "TINYINT(1)", name = "BREEDING_EXP")
@ColumnDefault("false")
private String breeding; //동물 길러본 경험
@Lob
@Column(nullable = false)
private String adopt_reason; //입양 이유
@Lob
private String add_comment; //추가 전달사항
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private AdoptStatus adopt_status; //입양 신청 상태
@Lob
private String cancel_reason; //입양 취소 이유
}
- 게시판 엔티티
@Entity
@Getter
@ToString
@Setter
public class Board extends BaseEntity {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id; //게시판 아이디
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member; //연관관계 매핑 - 멤버 (작성자 필요)
@Lob
@Column(nullable = false)
private String title; //게시판 제목
@Lob
@Column(nullable = false)
private String content; //게시판 글
@Column(nullable = false, columnDefinition = "int default 0")
private int views; //조회수
}
게시판과 관련된 엔티티는 기본적으로 생성날짜, 수정날짜를 포함하고 있으므로 BaseEntity를 만들어서 생성날짜와, 수정날짜를 상속하고 있다. 게시판과 댓글 엔티티를 설정할 때 헷갈렸던 부분이 작성자 필드를 따로 생성해야할 필요가 있는가 하는 것이었다. 사실 데이터베이스만 생각해보면 딱히 그럴 필요가 없고 어차피 조인을 해서 멤버의 이름을 반환하면 되는데 jpa와 함께 사용해서 객체의 입장에서 매핑을 하려다보니 머릿속이 꼬였던 것 같다. 기본 연관관계 매핑을 해주면서 fetch 타입을 LAZY로 전부 설정을 해뒀는데, 게시판과 댓글을 생각해보면 화면이 보이는 동시에 작성자명과 댓글 작성자가 보여야하기에 EAGER가 맞다고 생각했다. (현재 생각으로는 그게 맞는 것 같은데 큰 틀만 생각해놓은 상태이므로 구현하면서 잘못된 부분이라면 수정하여 정리하도록 하겠다.)
- 댓글 엔티티
@Entity
@Getter
@ToString
@Setter
public class Comment extends BaseEntity {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id; //댓글 아이디
@Lob
@Column(nullable = false)
private String comment; //댓글 내용
@ManyToOne
@JoinColumn(name = "BOARD_ID")
private Board board; //연관관계 매핑 - 게시판
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member; //연관관계 매핑 - 멤버 (댓글 작성자 필요)
}
정리
기본적인 엔티티 설정들까지 끝냈는데, 생각보다 엄청 오래 걸렸다. 빠르게 끝날거라는건 나의 큰 착각이였다.. 기본적인 엔티티 매핑까지만 해봤고 스프링부트에 적용해보는건 한번도 안해봤는데 오늘 매핑하면서 여러가지 기능들을 찾아보니까 생각보다 쉽지 않을 것 같다는 생각이 들었다. 추석 전까지 구현을 완료하는게 목표였는데, 시간적 여유가 생각보다 많지 않을 수도 있을거란 생각이 든다. 게시판 댓글 기능이 마냥 쉽지만은 않을 것 같은데 그래도 직접 부딪혀가며 해내는 과정에서 성장이 이루어질 수 있는다고 믿기에 기대가 되기도 한다. 팀 프로젝트를 하면서 스스로가 많이 성장했구나 하는걸 직접 느낄 수 있었기에, 이번엔 그 누구의 도움도 없이 혼자서 처음부터 끝까지 완수를 한다면 또 얼마나 많은 걸 배울 수 있을지 기대가 되기도 한다. 목표는 하루에 기능 하나씩 완료해서 블로그에 정리하기!! 최대한 스스로의 약속을 지킬 수 있도록 노력해보겠다. 💪
'기술블로그' 카테고리의 다른 글
FureverHomes 프로젝트 - 회원가입 2. 이메일 인증 (0) | 2023.09.21 |
---|---|
FureverHomes 프로젝트 - 회원 가입 1. 회원 저장과 중복 확인 (0) | 2023.09.19 |
AWS를 이용한 재배포 (0) | 2023.07.09 |
프로젝트와 교육과정 종료 후 짧은글 (0) | 2023.06.07 |
최종 프로젝트 종료 일주일 전 (1) | 2023.05.09 |