๐Ÿ“š TIL 23์ผ์ฐจ

temprmnยท2023๋…„ 6์›” 16์ผ
0
post-thumbnail

์˜ค๋Š˜์˜ ์ฃผ์š”์‚ฌํ•ญ (6/16, ๊ธˆ์š”์ผ)

  1. JPA ํ•™์Šต
  2. ๊ฐœ์ธ๊ณผ์ œ ๋งˆ๋ฌด๋ฆฌ

Spring-CRUD-๊ณผ์ œ-Lv1 : Blog

JPA ์ ์šฉ

JPA๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ๊นŒ์ง€๊ฐ€ ๊ณผ์ œ์ธ ๊ฒƒ ๊ฐ™์•„์„œ... ์–ด์ œ์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์กฐ๊ธˆ ๋” ์‚ด์„ ๋ถ™์—ฌ๋ณด์•˜๋‹ค.

1) ํ™˜๊ฒฝ ์„ค์ •

build.gradle

JPA๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ JDBC๋Š” ์‚ฌ์šฉํ•  ์ผ์ด ์—†์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์—, JPA ์„ค์ • ์ถ”๊ฐ€์™€ ํ•จ๊ป˜ JDBC ์„ค์ •์€ ์ง€์›Œ์ฃผ๊ธฐ๋กœ ํ–ˆ๋‹ค.

dependencies {
	
    /* ์ค‘๋žต */
	
	// JPA ์„ค์ •
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}

application.properties

1) database ์„ค์ •
spring.jpa.database=mysql
spring.datasource.username=root
spring.datasource.password=12345678
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 
2) jpa hibernate ์„ค์ •
spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

spring.jpa.hibernate.ddl-auto๋ฅผ create๋กœ ์„ค์ •ํ•ด์ฃผ๋ฉด, ๊ธฐ์กด ์ƒ์„ฑ๋˜์–ด์žˆ๋Š” ํ…Œ์ด๋ธ”์„ ๋ฌด์‹œํ•˜๊ณ  ์ƒˆ๋กœ์šด ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•ด์ค€๋‹ค.

2) Entity ์ˆ˜์ •

ํ…Œ์ด๋ธ” ์ •๋ณด๋ฅผ ์ฝ”๋“œ๊ฐ€ ์ธ์‹ํ•˜๊ณ  ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๋„๋ก @์• ๋…ธํ…Œ์ด์…˜์„ ํ™œ์šฉํ•˜์—ฌ ์ •๋ณด๋ฅผ ๋ช…์‹œํ•ด์ค€๋‹ค.

๊ธฐ์กด์— ์‚ฌ์šฉํ–ˆ๋˜ create table ๋ช…๋ น์–ด์™€ ๋ฐ”๋€ entity๋ฅผ ํ•จ๊ป˜ ์‚ดํŽด๋ณด์ž.

CREATE TABLE

create table post (
        id int not null auto_increment,
        
		createdAt DATETIME not null,
		modifiedAt DATETIME not null,
        
		title varchar(255) not null,
        content varchar(500) not null,
        author varchar(255) not null,
		password varchar(255) not null,
        
        primary key (id)
);

Blog Entity

@Entity // JPA๊ฐ€ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” Entity ํด๋ž˜์Šค ์ง€์ •
@Getter
@Setter
@Table(name = "post") // ๋งคํ•‘ํ•  ํ…Œ์ด๋ธ”์˜ ์ด๋ฆ„์„ ์ง€์ •
@NoArgsConstructor
public class Blog extends Timestamped {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id; // id
    @Column(name = "title", nullable = false)
    private String title; // ์ œ๋ชฉ
    @Column(name = "content", nullable = false)
    private String content; // ๋‚ด์šฉ
    @Column(name = "author", nullable = false)
    private String author; // ์ž‘์„ฑ์ž
    @Column(name = "password", nullable = false, length = 12)
    private String password; // ๋น„๋ฐ€๋ฒˆํ˜ธ


    /* ์ƒ์„ฑ์ž์™€ ๋ฉ”์„œ๋“œ๋Š” ๋™์ผํ•œ ํ˜•ํƒœ์ด๋‹ค */

}

JPA์„ ํ™œ์šฉํ•˜๋ฉด @Column๋“ฑ๊ณผ ๊ฐ™์€ ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด, create table ๋ช…๋ น๋ฌธ๊ณผ ๋™์ผํ•œ ํšจ๊ณผ๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด์„œ

title varchar(255) not null,

์€,

@Column(name = "title", nullable = false)
private String title; // ์ œ๋ชฉ

์ด์ œ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ Java-Spring ์ฝ”๋“œ๊ฐ€ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

3) Timestamped: ์‹œ๊ฐ„ ํƒ€์ž… ๋ณ€๊ฒฝ

์‹œ๊ฐ„ ํƒ€์ž…์„ ๋‹ค์‹œ varchar์—์„œ DATETIME์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ๋‹ค. DATETIME์œผ๋กœ ์ €์žฅ๋˜์–ด๋„, ๋ฐ€๋ฆฌ์ดˆ๊นŒ์ง€ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.

๋ณ€๊ฒฝ์— ๋”ฐ๋ผ ์‹œ๊ฐ„ ์ •๋ณด๋ฅผ ์ €์žฅํ•  entity๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด Timestamped entity๋Š” ์ถ”์ƒ ํด๋ž˜์Šค๋กœ, Blog๊ฐ€ ์ƒ์† ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

Timestamped Entity

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {

    @CreatedDate
    @Column(updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime modifiedAt;
}

4) Repository ์ˆ˜์ • (JpaRepository)

BlogRepository๋Š” JpaRepository๋ฅผ ์ƒ์†ํ•˜๊ฒŒ ๋จ์œผ๋กœ์จ, ๋”ฐ๋กœ ๊ตฌํ˜„๋ถ€๋ฅผ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์–ด์กŒ๋‹ค.

public interface BlogRepository extends JpaRepository<Blog, Integer> {
}

์ด๋Ÿฌํ•œ ์ƒํƒœ๋งŒ์œผ๋กœ๋„ JpaRepository๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ๋Œ€๋ถ€๋ถ„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. ํ•˜์ง€๋งŒ JpaRepository ๊ธฐ๋ณธ ์ œ๊ณต ๊ธฐ๋Šฅ ์™ธ์—, ๋‹ค๋ฅธ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ๋“ค์ด ํ•„์š”ํ•ด์ง„๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ์„ ์–ธํ•จ์œผ๋กœ์จ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

public interface BlogRepository extends JpaRepository<Blog, Integer> {

	// ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ - CreatedAt (์ƒ์„ฑ ์‹œ๊ฐ„)์„ ๊ธฐ์ค€์œผ๋กœ
    List<Blog> findAllByOrderByCreatedAtDesc();
    
    // Id์™€ Password๋ฅผ ์‚ฌ์šฉํ•ด Blog๋ฅผ ์ฐพ์•„์„œ ๋ฐ˜ํ™˜
    Blog findByIdAndPassword(Integer id, String password);    
}

๋‚˜๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” findById() ์™ธ์—, ๋น„๋ฐ€๋ฒˆํ˜ธ(password)๋„ ํ•จ๊ป˜ ์กฐํšŒํ•˜๊ณ  ์‹ถ์–ด์„œ, findByIdAndPassword()๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ•จ๊ป˜ ๋งŒ๋“ค์–ด์„œ ์„ ์–ธํ•ด์ฃผ์—ˆ๋‹ค.

Repository์—์„œ ๊ตฌํ˜„ํ–ˆ๋‹ค๋ฉด, ํ˜ธ์ถœ์€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Service๊ฐ€ ํ•œ๋‹ค.

findByIdAndPassword()๋ฅผ ํ™œ์šฉํ•ด id์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ•จ๊ป˜ ์ฒดํฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

@Transactional
public BlogResponseDto updateBlogPost(Integer id, BlogRequestDto requestDto) {

    // 1. ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์ด DB์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
    Blog blog = blogRepository.findByIdAndPassword(id, requestDto.getPassword());
    if(blog != null) {
    
        // 2. ์กด์žฌํ•˜๋ฉด blog ์ˆ˜์ •
        blog.update(requestDto); // DB ์ €์žฅ
        BlogResponseDto blogResponseDto = new BlogResponseDto(blog);
        
        return blogResponseDto;
        
    } else {
        throw new IllegalArgumentException("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
    }
}

ํ…Œ์ด๋ธ”์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” @Transactional ์• ๋…ธํ…Œ์ด์…˜์ด ํ•„์š”ํ•˜๋‹ค.

createdAt์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค.

// BlogService.java

public List<BlogResponseDto> getBlogPosts() {

	// DB ์กฐํšŒ
    return blogRepository.findAllByOrderByCreatedAtDesc().stream().map(BlogResponseDto::new).toList();
}

Postman์„ ํ†ตํ•œ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ํ™”๋ฉด

๊ฒŒ์‹œ๊ธ€ ์ „์ฒด ์กฐํšŒ /blog/posts

์„ ํƒ ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ • /blog/post/{id}

์„ ํƒ ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ • ์‹คํŒจ blog/post/{id}

์„ ํƒ ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ /blog/post/{id}

๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ /blog/post

์„ ํƒ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ blog/post/{id}

profile
`ISFJ` T 49% F 51% /

0๊ฐœ์˜ ๋Œ“๊ธ€