Java/Spring 조각 기록(3)
DB, Java
application.properties 파일에 DB 관련 설정 중 모르는 것이 나와 찾아보았다.
// application.properties
spring:
jpa:
generate-ddl : true
hibernate :
ddl-auto : update
show-sql : true
JPA ?
jpa
란 Java Persistence API의 준말로 현재 자바 진영의 ORM 기술 표준으로 '인터페이스 모음'이라고 한다.
ORM ?
ORM
은 Object-Relational Mapping의 준말. 객체는 객체대로, 관계형 DB는 관계형 DB대로 설계하면 ORM이 중간에서 매핑해주는 것.
정리해보면 Java로 관계형 데이터베이스(RDB)에 접근할 수 있게 해주는 중간자 역할인 것 같다.
참조한 문서를 조금 더 읽어보니 JPA는 내부에서 JDBC를 이용하여 DB와 통신한다고 한다.
Hibernate ?
앞서 언급했듯 JPA는 인터페이스 모음이기에 구현체가 필요하다. hibernate는 그러한 구현체 중 하나라고 한다.
즉 위의 코드는 jpa를, 그 중 hibernate를 구현체로 삼아서 사용하겠다는 의미겠지.
(ddl
은 뭘까? Data Definition Language로 '데이터 정의어'라고 한다. CREATE, ALTER, DROP, RENAME 등의 명령어가 이에 해당한다고 한다.)
spring.jpa.generate-ddl : true
는 @Entity
가 명시된 class를 찾아 ddl을 생성하고 실행한다고 한다.
spring.jpa.hibernate.ddl-auto: update
는 Entity class와 DB 구조를 비교하여 DB 구조에 없는 것을 자동으로 추가한다고 한다.
즉 위의 일련의 과정들은 Java에서의 변경이 DB에서도 이루어질 수 있도록 하는 설정이라는 것
@Entity
@Entity
의 적용
@Entity
의 적용다음의 코드를 살펴보자.
@Entity
public class Member{
}
여기까지만 코드를 쓰면 오류가 발생한다 → PK(Primary Key)가 없다는 것.
@Entity
가 붙은 클래스를 RDB와 연결한다고 했다. RDB 테이블에는 PK가 반드시 필요하다.
코드를 다음처럼 수정하자.
@Data
@Entity
public class Member{
@Id
private String userId;
private String userPw;
private String userName;
}
@Data
로 getter, setter 등의 설정을 skip하고(알아서 해준다), @Entity
로 JPA가 DB와 연동할 클래스라는 것을 알게 해준다. (@Id
를 통해 PK를 설정하는 것 같다.)
Entity와 Repository
Entity와 Respository는 보통 세트를 이룬다.
위에서 만든 Member 클래스를 entity라는 패키지에 넣고, 해당 패키지와 병렬적으로(?) repository 패키지를 만든다.
그 후, MemberRepository라는 인터페이스를 그 패키지 안에 만든다.
package ~.fastlms.member;
public interface MemberRepository extends JpaRepository<Member, String>{ // 'Member'는 table명, 'String'은 key
}
실행시키고 Database 탭을 새로고침하면 tables 밑에 member라는 테이블에 생성되었고, 해당 table 밑 columns 폴더에 우리가 설정한 parameter에 대한 data 종류들(user_id, user_pw, user_name)이 칼럼으로 생성된 것을 확인할 수 있다.
(table은 만들어졌는데 data가 없는 이유는 무엇일까? → 아직 DB에 저장을 하지 않았기 때문이다. 우리는 지금까지 JPA 설정을 하고, Member클래스에 @Entity를 붙이고, 세트로 사용할 MemberRepository 인터페이스를 생성했을 뿐이다.)
🤨 JpaRepository는 또 뭐야?
spring에서 지원하는 JPA를 편리하게 사용할 수 있도록 지원하는 프로젝트이다. 구현 클래스를 만들지 않고 인터페이스만으로도 DB에 접근할 수 있도록 한다.
JpaRepository를 상속 받으면 여러 메소드들을 사용할 수 있는데 이 메소드 이름만으로 쿼리를 생성할 수 있다.
예를 들어
public interface MemberRepository extends JpaRepository<Member, String>{
Optional<Member> findByAge(String age);
}
위의 코드는 Member 클래스로 인해 만들어진 테이블에서 age를 key로 data를 추출할 수 있다.
Optional
에 대해서는 다음을 참고
https://mangkyu.tistory.com/70
<form>으로 받은 정보를 DB에 저장
Java/Spring 조각 기록(2)과 위의 내용을 참고하여 입력받은 정보들을 DB에 저장하는 방법을 알아보자.
@Data
public class MemberInputs{
private String ID;
private String PW;
private String name;
}
우선 입력받을 값들에 대해 MemberInputs에 멤버변수를 만들어 놓고
(또 @Data를 통해 getter, setter를 선언하지 않고도 사용할 수 있도록 한다.)
@Data
@Entity
public class Member{
@Id
private String userId;
private String userPw;
private String userName;
}
Member 클래스를 생성하고 @Entity를 통해 JPA에게 이 클래스를 DB와 연동할거라고 알려주고
@PostMapping("...")
public String memberRegisterSubmit(MemberInputs infos){
Member member = new Member();
member.setUserId(infos.getID());
member.setUserPw(infos.getPW());
member.setUserName(infos.getName());
memberRepository.save(member);
return "...";
}
MemberController 내부의 메소드에서 "..." url에서의 post요청이 발생하였을 때 MemberInputs 클래스형의 infos(여기엔 ID, PW, name parameter에 사용자가 입력하고 post 요청을 보낸 값들이 들어있다)를 이용하여 각 parameter에 대한 값을 Member 인스턴스 member 멤버 변수에 할당한다.
그 후, memberRepository에 member를 save()하는데, memberRepository가 JpaRepository를 상속받았기 때문에 별다른 구현체 없이도(memberRepository 내에 save라는 메소드를 구현하지 않았다) save 메소드를 사용하여 쿼리를 작성하고, DB member 테이블에 data를 저장할 수 있다.
❗️ 위 방법의 문제점
userId의 값을 [email protected]으로 입력한 사람이 있다고 가정하자. 해당 data는 DB에 저장되었을 것이다.(user_id column에 [email protected]이 저장된 채로)
그 후 또다른 사람이 userId의 값을 [email protected]으로 입력했다고 생각해보자. 그러면 해당 data가 DB에 저장되는데 이 때 [email protected]였던 이전 사람의 data는 새로운 사람의 data로 덮어써진다. user_id는 PK라 중복이 허용되지 않는다. 하지만 잘못된 입력으로 애꿎은 사람의 data만 덮어씌워진 것이다.
🧐 이를 방지하려면 어떻게 해야할까?
fastlms/member 아래에 service라는 디렉토리를 만들고 해당 디렉토리 내에 MemberService라는 인터페이스를 만들자.
package ~.fastlms.member.service //'~'는 실제로 쓰는 것이 아닌 생략의 의미
import ~.fastlms.member.model.MemberInputs;
public interface MemberService{
boolean register(MemberInputs infos);
}
그리고 해당 인터페이스의 구현체를 만들어야 한다. 앞으로 구현체들을 저장하기 위해 service 디렉토리 아래에 impl 패키지를 만들자. 그리고 해당 패키지 내에 MemberServiceImpl 클래스를 만든다.
package ~.fastlms.member.service.impl
import ~.fastlms.member.model.MemberInputs;
import org.springframework.stereotype.Service;
//(후략)
@RequiredArgsConstructor // 아래의 설명을 읽어볼 것. 간략히 말해 지금 이 코드는 아래에서 선언한 memberRepository에 대한 생성자를 별도로 명시하여 만들지 않아도 되게 한다.
@Service
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
@Override
public boolean register(MemberInputs infos){
Optional<Member> optionalMember = memberRespository.findById(infos.getUserId())
if(optionalMember.isPresent()){ // 지금 입력받은 datas에 대해 userId값을 기준으로 DB 내 datas를 조회해봤을 때 존재한다면
return false; // 바로 false를 return하고 메소드 종료
} // 즉 이미 존재하는 userId(여기서는 email)를 입력할 경우 덮어써지지 않게끔.
Member member = new Member();
member.setUserId(infos.getID());
member.setUserPw(infos.getPW());
member.setUserName(infos.getName());
memberRepository.save(member);
return true; //회원 정보 DB 저장 여부(true)
}
}
그 이후 MemberController 코드를 다음처럼 수정하자.
@Controller
public class MemberController{
private final MemberService memberService;
public MemberController(MemberService memberService){
this.MemberService = memberService;
} //생성자
}
(이때 보다 간편한 방법이 있다. lombok의 @RequiredArgsConstructor
를 이용하는 것)
@RequiredArgsConstructor// 생성자를 만들지 않아도 된다.
@Controller
public class MemberController{
private final MemberService memberService;
@PostMapping("...")
public String memberRegisterSubmit(MemberInputs infos){
boolean result = memberService.register(infos); //result에는 회원 정보 DB 저장 여부가 담긴다. false라면 동일한 user_id가 이미 존재하기 때문에 저장이 되지 않았다는 뜻. true면 동일한 user_id가 없고 때문에 입력받은 data들이 Member 인스턴스로 담겨 MemberRepository의 save메소드를 이용해 DB에 저장되었다는 뜻이다.
return "...."; // 회원 정보 저장 완료와 미완료 시 표기되는 html을 다르게 해야할 것.
}
여기까지 왔다면 패키지들은 다음과 같을 것이다.
- controller
MemberController
- entity
Member
- model
MemberInputs
- repository
MemberRepository
- service
MemberService
- Impl
MemberServiceImpl
Summary
📌 JPA, @Entity, class
- ORM 기술 중 하나인 JPA를 이용(Hibernate로 구현)하여 Java 언어로 DB에 접근할 수 있다.
- DB에 table로 만들고 싶은 클래스에
@Entity
를 붙인다.
- @Id로 PK를 설정한다.
📌 interface Repository, JpaRepository
- JpaRepository를 상속받음으로써 메소드 구현 없이 선언만으로 DB에 쿼리를 쏠 수 있다.
- Ex.
- Optional<Member> findById(String id);
- Optional<Member> findByAge(Integer age);
📌 Service, 중복 방지
- 중복된 ID(email)을 입력받을 경우 기존 data가 새로운 data로 덮어씌워질 위험성이 있다.
- MemberSerive interface, MemberServiceImpl class를 만들고 메소드를 이용하여 true or false를 return한다.
- true에 대해서만 DB에 저장이 되게 한다.
- 해당 return 값을 이용하여 보여줄 페이지를 다르게 한다.
- true 시 인증 단계로, false 시 회원 가입 불가 메세지를 띄우는 식으로.
Questions
🤔 Member, MemberInputs, MemberRepository 등으로 나누어서 (중복되는 것처럼 보이는) 작업들을 굳이 수행해야 하는 이유는 뭘까?
- 지금까지 배운대로 생각해봤을 때...
- Member 클래스는 DB에 table을 만드는 데에 쓰인다.
@Entity
를 붙이고 JPA를 이용하여 DB에 table명이 'Member'이고 column명이 'userId', 'userPw', 'name'인 column 구조를 만든다.
- MemberInputs는
@Data
를 이용하여 보다 쉽게 입력받은 값에 접근하기 위해 사용한다.
- MemberRepository는 interface이지만 JpaRepository를 상속받음으로써 구현체 없이 메소드를 선언하기만 해도 자동으로 DB에 대한 쿼리를 쏠 수 있다.
- 순서는 다음과 같다.
Member
DB에 table을 생성해둔다.(table명은 'Member', column들은 'user_id', 'user_pw', 'name')
→ register.html
ID, PW, name 등을 입력 받는다
→ MemberInputs
POST 요청 시 클래스 내 멤버변수와 일치하는 parameter들을 가지고 있는다
→ Entity인 Member
에 각각의 값들이 저장된다
→ MemberRepository
상속받은 JpaRepository
때문에 save
메소드를 사용할 수 있고 이를 이용해 Member
인스턴스의 각 값들을 DB에 쏴줄 수 있다
Member
, MemberRepository
는 필요하다고 생각한다.
@Entity
를 붙여 table 구조를 만들 class가 필요하고,
JpaRepository
를 상속받을 interface가 필요하다.
MemberInputs
는 없어도 대체할 방법이 있다.
🤔 MemberService class를 선언하지 않고 MemberService interface, MemberServiceImpl class 두 단계를 거치는 이유가 뭘까?
MemberRepository
의 경우에는 JpaRepository의 사용법이 그러하다고 생각했다.(인터페이스와 구현되지 않고 선언만 된 메소드만으로 쿼리문을 만들어낼 수 있도록.)
- 단순히 ID 중복체크를 하는 로직은
MemberService(class)
로도 충분히 구현할 수 있는 것 같은데 굳이 interface를 쓴 이유는?
- Java 'interface' 챕터를 더 공부해봐야겠다.
🤔 MVC 패턴 등 웹 사이트를 만드는 패턴이 있다고 들었다.
- 구체적으로 어떤 건지 아직 감이 잘 오지 않는다. 대략적으로 어떤 것이 view이고 controller인지는 알겠는데 model이 정확히 어떤 의미인지 와닿지가 않는다.
참조
- https://gmlwjd9405.github.io/2019/08/04/what-is-jpa.html
- https://gmlwjd9405.github.io/2019/08/03/reason-why-use-jpa.html
- https://ko.wikipedia.org/wiki/관계형_데이터베이스
- https://ko.wikipedia.org/wiki/JDBC
- https://velog.io/@owljoa/수정필요-JPA-Hibernate-초기-데이터-생성-
- https://data-make.tistory.com/621
- https://m.blog.naver.com/scw0531/220988401816
Author And Source
이 문제에 관하여(Java/Spring 조각 기록(3)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@ivorrr987/JavaSpring-조각기록3
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
MemberController
Member
MemberInputs
MemberRepository
MemberService
- Impl
MemberServiceImpl
📌 JPA, @Entity, class
- ORM 기술 중 하나인 JPA를 이용(Hibernate로 구현)하여 Java 언어로 DB에 접근할 수 있다.
- DB에 table로 만들고 싶은 클래스에
@Entity
를 붙인다. - @Id로 PK를 설정한다.
- DB에 table로 만들고 싶은 클래스에
📌 interface Repository, JpaRepository
- JpaRepository를 상속받음으로써 메소드 구현 없이 선언만으로 DB에 쿼리를 쏠 수 있다.
- Ex.
- Optional<Member> findById(String id);
- Optional<Member> findByAge(Integer age);
- Ex.
📌 Service, 중복 방지
- 중복된 ID(email)을 입력받을 경우 기존 data가 새로운 data로 덮어씌워질 위험성이 있다.
- MemberSerive interface, MemberServiceImpl class를 만들고 메소드를 이용하여 true or false를 return한다.
- true에 대해서만 DB에 저장이 되게 한다.
- 해당 return 값을 이용하여 보여줄 페이지를 다르게 한다.
- true 시 인증 단계로, false 시 회원 가입 불가 메세지를 띄우는 식으로.
- MemberSerive interface, MemberServiceImpl class를 만들고 메소드를 이용하여 true or false를 return한다.
Questions
🤔 Member, MemberInputs, MemberRepository 등으로 나누어서 (중복되는 것처럼 보이는) 작업들을 굳이 수행해야 하는 이유는 뭘까?
- 지금까지 배운대로 생각해봤을 때...
- Member 클래스는 DB에 table을 만드는 데에 쓰인다.
@Entity
를 붙이고 JPA를 이용하여 DB에 table명이 'Member'이고 column명이 'userId', 'userPw', 'name'인 column 구조를 만든다.
- MemberInputs는
@Data
를 이용하여 보다 쉽게 입력받은 값에 접근하기 위해 사용한다.
- MemberRepository는 interface이지만 JpaRepository를 상속받음으로써 구현체 없이 메소드를 선언하기만 해도 자동으로 DB에 대한 쿼리를 쏠 수 있다.
- 순서는 다음과 같다.
Member
DB에 table을 생성해둔다.(table명은 'Member', column들은 'user_id', 'user_pw', 'name')
→ register.html
ID, PW, name 등을 입력 받는다
→ MemberInputs
POST 요청 시 클래스 내 멤버변수와 일치하는 parameter들을 가지고 있는다
→ Entity인 Member
에 각각의 값들이 저장된다
→ MemberRepository
상속받은 JpaRepository
때문에 save
메소드를 사용할 수 있고 이를 이용해 Member
인스턴스의 각 값들을 DB에 쏴줄 수 있다
Member
, MemberRepository
는 필요하다고 생각한다.
@Entity
를 붙여 table 구조를 만들 class가 필요하고,
JpaRepository
를 상속받을 interface가 필요하다.
MemberInputs
는 없어도 대체할 방법이 있다.
🤔 MemberService class를 선언하지 않고 MemberService interface, MemberServiceImpl class 두 단계를 거치는 이유가 뭘까?
MemberRepository
의 경우에는 JpaRepository의 사용법이 그러하다고 생각했다.(인터페이스와 구현되지 않고 선언만 된 메소드만으로 쿼리문을 만들어낼 수 있도록.)
- 단순히 ID 중복체크를 하는 로직은
MemberService(class)
로도 충분히 구현할 수 있는 것 같은데 굳이 interface를 쓴 이유는?
- Java 'interface' 챕터를 더 공부해봐야겠다.
🤔 MVC 패턴 등 웹 사이트를 만드는 패턴이 있다고 들었다.
- 구체적으로 어떤 건지 아직 감이 잘 오지 않는다. 대략적으로 어떤 것이 view이고 controller인지는 알겠는데 model이 정확히 어떤 의미인지 와닿지가 않는다.
참조
- https://gmlwjd9405.github.io/2019/08/04/what-is-jpa.html
- https://gmlwjd9405.github.io/2019/08/03/reason-why-use-jpa.html
- https://ko.wikipedia.org/wiki/관계형_데이터베이스
- https://ko.wikipedia.org/wiki/JDBC
- https://velog.io/@owljoa/수정필요-JPA-Hibernate-초기-데이터-생성-
- https://data-make.tistory.com/621
- https://m.blog.naver.com/scw0531/220988401816
Author And Source
이 문제에 관하여(Java/Spring 조각 기록(3)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@ivorrr987/JavaSpring-조각기록3
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
- Member 클래스는 DB에 table을 만드는 데에 쓰인다.
@Entity
를 붙이고 JPA를 이용하여 DB에 table명이 'Member'이고 column명이 'userId', 'userPw', 'name'인 column 구조를 만든다. - MemberInputs는
@Data
를 이용하여 보다 쉽게 입력받은 값에 접근하기 위해 사용한다. - MemberRepository는 interface이지만 JpaRepository를 상속받음으로써 구현체 없이 메소드를 선언하기만 해도 자동으로 DB에 대한 쿼리를 쏠 수 있다.
Member
DB에 table을 생성해둔다.(table명은 'Member', column들은 'user_id', 'user_pw', 'name')→
register.html
ID, PW, name 등을 입력 받는다→
MemberInputs
POST 요청 시 클래스 내 멤버변수와 일치하는 parameter들을 가지고 있는다→ Entity인
Member
에 각각의 값들이 저장된다→
MemberRepository
상속받은 JpaRepository
때문에 save
메소드를 사용할 수 있고 이를 이용해 Member
인스턴스의 각 값들을 DB에 쏴줄 수 있다Member
, MemberRepository
는 필요하다고 생각한다.@Entity
를 붙여 table 구조를 만들 class가 필요하고,JpaRepository
를 상속받을 interface가 필요하다.MemberInputs
는 없어도 대체할 방법이 있다. MemberRepository
의 경우에는 JpaRepository의 사용법이 그러하다고 생각했다.(인터페이스와 구현되지 않고 선언만 된 메소드만으로 쿼리문을 만들어낼 수 있도록.)MemberService(class)
로도 충분히 구현할 수 있는 것 같은데 굳이 interface를 쓴 이유는?- Java 'interface' 챕터를 더 공부해봐야겠다.
- https://gmlwjd9405.github.io/2019/08/04/what-is-jpa.html
- https://gmlwjd9405.github.io/2019/08/03/reason-why-use-jpa.html
- https://ko.wikipedia.org/wiki/관계형_데이터베이스
- https://ko.wikipedia.org/wiki/JDBC
- https://velog.io/@owljoa/수정필요-JPA-Hibernate-초기-데이터-생성-
- https://data-make.tistory.com/621
- https://m.blog.naver.com/scw0531/220988401816
Author And Source
이 문제에 관하여(Java/Spring 조각 기록(3)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@ivorrr987/JavaSpring-조각기록3저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)