포스코x코딩온 웹 풀스택 양성과정

[포스코x코딩온] 웹 풀스택 과정 18주차 회고 | Spring JPA

Codult 2024. 4. 18. 23:45
728x90

📌 JPA

JPA(Java Persistence API)는 Java 진영의 ORM 기술 표준으로, 적합한 SQL을 생성하여 DB에 전달하고, 객체를 자동으로 매핑해준다. (SQL을 직접 작성할 필요가 없음)

장점

  • 생산성이 뛰어나고 유지보수가 용이하다.
  • DBMS에 대한 종속성이 줄어든다.

단점

  • JPA의 장점을 살려 잘 사용하기 위해서는 학습 비용이 높고, 복잡한 쿼리를 사용할 때 불리하다.
  • 잘못 사용할 경우 SQL을 직접 사용하는 것보다 성능이 떨어질 수 있다.

📌 Entity

데이터베이스에 쓰일 필드와 여러 Entity 간의 관계를 설정하는 것이다.
(myBatis에서의 sql, xml, domain 역할)

  • @Entity를 이용해 해당 클래스가 Entity임을 알려준다.
  • JPA에 정의된 필드를 바탕으로 테이터베이스에 테이블을 만들 수 있다.

Entity 관련 Annotation

@Entity: 데이터베이스의 table임을 의미한다.
@Table: 테이블 이름을 명시한다. (작성하지 않는 경우, Entity 클래스명이 테이블명이 됨)
Id: primary key를 의미한다.
GeneratedValue: primary key의 생성 전략을 설정한다. (ex. startegy = GenerationType.Auto)
Column: table의 컬럼을 의미한다. (length, nullable 등 속성을 변경할 수 있다.)

📌 Repository

Entity에 의해 생성된 DB에 접근하는 메소드를 사용하기 위한 인터페이스이다. (myBatis에서의 mapper 역할)

  • JpaRepository를 상속받으면, 기본적인 DB 접근 메소드를 사용할 수 있다.
    (ex. findAll(), findById(), findBy컬럼명(), save())
  • JpaRepository에서 기본적으로 제공하는 쿼리는 repository에서 별도로 작성하지 않아도 service에서 바로 사용할 수 있다.

📌 Optional

Null의 가능성이 있는 객체를 감싸는 Wrapper 클래스이다.
Optional<T>

  • Option 변수 내부에는 null이 아닌 T객체가 있을 수 있다.
  • 또는 null이 있을 수 있다.
  • 즉, Optional 클래스는 여러가지 API를 제공해 Null의 가능성이 있는 객체를 다룰 수 있다.
  • Optional 클래스의 값을 가져오기 위해서는 .get()으로 접근 후 가져와야 한다.

Optional 클래스의 주요 메소드

Present(): 값이 있으면 true, 없으면 false를 반환한다.
orElse(): 값이 있으면 그 값을 반환하고, 없으면 ()안에 인자로 넘겨진 값을 반환한다. (ex. ~~.orElse("저장된 값이 없습니다.")
orElseThrow(): 값이 있으면 그 값을 반환하고, 없으면 ()안의 throw 함수(예외처리 함수)를 실행한다.
of(): 명시된 값을 갖는 Optional 객체를 반환한다. (주의: null이 아님을 확신하는 값이어야 함)

 

📌 JPA 사용하기

dependency 라이브러리 추가 & application.properties 수정
→ Entity 생성 (테이블 설정, getter, setter)
→ Repository 생성 (DB에 접근하는 메소드를 사용하기 위한 인터페이스)
→ Service 생성
→ Controller 생성

dependency 추가

// build.gradle
	implementation: 'org.springframework.boot:spring-boot-starter-data-jpa'

application.properties 수정

// application.properties
	spring.jpa.show-sql=true	// jpa에서 사용할 수 있는 sql문을 보여준다.
    spring.jpa.hibernate.ddl-auto=create	// 서버 실행 시, 설정한 테이블이 생성되도록 한다.
    // none: not create, create: auto create, update: only update

UserEntity

이전 mybatis에서의 sql, xml, domain 파일의 역할을 한다.

// UserEntity.java

@Entity
@Table(name="myTable")
public class MemberEntity {
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    
    @Column(length = 31, nullable = false)
    private String userId;
    
    @Column(length = 31, nullable = false)
    private String name;
    
    @Column(nullable = false)
    private String password;
    
    // 속성을 기본값으로 유지한다면, @Column 표기하지 않아도 된다.
    private String email;
    
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    
    public String getUserId() { return userId; }
    public void setUserId(String userId) { this.userId = userId; }
    
    // ...
    // name, password, email의 getter, setter 함수
}

repository

// MemberRepository.java

@Repository
public interface MemberRepository extends JpaRepository<MemberEntity, Integer> {
	MemberEntity findByUserIdAndPassword(String userId, String password);
    Optional<MemberEntity> findByUserId(String userId);
}

Service

// MemberService.java

@Service
public class MemberService {
	@Autowired
    private MemberRepository memberRepository;
    
    public void insertNewMember(MemberDTO memberDTO){
    	MemberEntity member = new MemberEntity();
        member.setUserId(memberDTO.getUserId());
        member.setName(memberDTO.getName());
        member.setPassword(memberDTO.getPassword());
        
        // .save()와 같은 기본 JpaRepository 쿼리는 repository에서 별도로 작성하지 않아도 바로 사용할 수 있다.
        memberRepository.save(member);
    }
    
    public boolean checkMember(MemberDTO memberDTO) {
    	MemberEntity member = memberRepository.findByUserIdAndPassword(memberDTO.getUserID(), memberDTO.getPassword());
        if (member != null) return true;
        else return false;
    }
    
    public Optional<MemberDTO< getProfile(String userId) {
    	Optional<MemberEntity> member = memberRepository.findByUserId(userId);
        if (member.isPresent()){
        	MemberDTO data = new MemberDTO();
            data.setId(member.get().getId());
            data.setUserId(member.get().getUserId());
            data.setPassword(member.get().getPassword());
            data.setName(member.get().getName());
            return Optional.of(data);
        } else {
        	return Optional.empty();
        }
    }
    
    public boolean updateProfile(MemberDTO memberDTO) {
    	Optional<MemberEntity> member = memberRepository.findById(memberDTO.getId());
        if (member.isPresent()){
        	MemberEntity data = member.get();
            data.setName(memberDTO.getName());
            data.setPassword(memberDTO.getPassword());
            data.setUserId(memberDTO.getUserId());
            memberRepository.save(data);
            return true;
        } else return false;
    }
    
    public void deleteProfile(MemberDTO memberDTO) {
    	memberRepository.deleteById(memberDTO.getId());
    }
}

Controller

// MemberController.java

@Controller
public class MemberController {
	@Autowired
    private MemberService memberService;
    
    @GetMapping("/profile")
    public String getProfile(@RequestParam String userId, Model model) {
    	Optional<MemberDTO> member = memberService.getProfile(userId);
        if (member.isPresent()){
        	model.addAttribute("member", member.get());
            return "profile";
        } else {
        	return "error";
        }
    }
    
    @PostMapping("/register")
    @ResponseBody
    public void postRegister(@requestBody MemberDTO memberDTO) {
    	MemberDTO member = new MemberDTO();
        member.setUserId(memberDTO.getUserId());
        member.setName(memberDTO.getName());
        member.setPassword(memberDTO.getPassword());
        memberService.insertNewMember(member);
    }
    
    @PostMapping("/login")
    @ResponseBody
    public boolean postLogin(@RequestBody MemberDTO memberDTO) {
    	MemberDTO memberToCheck = new MemberDTO();
        memberToCheck.setUserId(memberDTO.getUserId());
        memberToCheck.setPassword(memberDTO.getPassword());
        return memberService.checkMember(memberToCheck);
    }
    
    @PostMapping("/profile")
    @ResponseBody
    public boolean postEdit(@RequestBody MemberDTO memberDTO) {
    	MemberDTO member = new MemberDTO();
        member.setId(membersDTO.getId());
        member.setUserId(memberDTO.getUserId());
        member.setName(memberDTO.getName());
        member.setPassword(memberDTO.getPassword());
        return memberService.updateProfile(member);
    }
    
    @PostMapping("/delete")
    @ResponseBody
    public void postDelete(@RequestBody MemberDTO memberDTO){
        MemberDTO member = new MembersDTO();
        member.setId(memberDTO.getId());
        member.setUserId(memberDTO.getUserId());
        member.setName(memberDTO.getName());
        member.setPassword(memberDTO.getPassword());
        memberService.deleteProfile(member);
    }
}
728x90