๐Ÿ’ป IT/์ž‘์€ ์ง€์‹๋“ค

Spring boot ๊ธฐ์ดˆ ๋ฐ ์„ค์ •

Record_er 2024. 4. 21. 00:09

๋ณต์Šต์šฉ

 

localhost:8080

localhost(=127.0.0.1) : ๋‚ด ์ปดํ“จํ„ฐ IP ์ฃผ์†Œ

8080 : ํฌํŠธ ๋ฒˆํ˜ธ

 

 

 

์• ๋„ˆํ…Œ์ด์…˜(annotation)

@Controller :  ์ปจํŠธ๋กค๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

@GetMapping("/hello") : localhost:8080/hello

@ResponseBody : ๋ฉ”์„œ๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๊ฐ€ ๋ฌธ์ž์ž„์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.

@RequiredArgsConstructor : ํ•„์š”ํ•œ ์ƒ์„ฑ์ž๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค. (@Setter ํ•„์š” X)

 

 

 

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

Spring Starter Project๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ๋‚˜ํƒ€๋‚˜๋Š” ๊ธฐ๋ณธ ๊ตฌ์กฐ๋‹ค.

 

  • src/main/java : ์ž๋ฐ” ํŒŒ์ผ์„ ์ €์žฅํ•˜๋Š” ๊ณต๊ฐ„ (์ปจํŠธ๋กค๋Ÿฌ, ํผ๊ณผ DTO, DB Entity, ์„œ๋น„์Šค)
  • SbbApplication.java : ์‹œ์ž‘์„ ๋‹ด๋‹นํ•˜๋Š” ํŒŒ์ผ์ด๋‹ค (ํ”„๋กœ์ ํŠธ๋ช… + Application์œผ๋กœ ์ž๋™ ์ƒ์„ฑ๋จ)
  • src/main/resources : HTML, CSS, JS, ํ™˜๊ฒฝํŒŒ์ผ์„ ์ €์žฅํ•˜๋Š” ๊ณต๊ฐ„
  • templates : ์ž๋ฐ” ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋Š” HTMLํ˜•์‹ ํŒŒ์ผ
  • static : CSS, JS, IMG(jpg,png) ๋“ฑ์„ ์ €์žฅ
  • application.properties : ํ”„๋กœ์ ํŠธ ํ™˜๊ฒฝ ์„ค์ • (ํ™˜๊ฒฝ ๋ณ€์ˆ˜, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •)
  • src/test/java : ํ…Œ์ŠคํŠธ์ฝ”๋“œ ์ €์žฅ ๊ณต๊ฐ„
  • bundle.gradle : gradle์ด ์‚ฌ์šฉํ•˜๋Š” ํ™˜๊ฒฝ ํŒŒ์ผ (ํ”Œ๋Ÿฌ๊ทธ์ธ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์ •)

 

 

ORM(Object-Relational Mapping)

  • SQL์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ์ž๋ฐ” ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์–ด ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ORM์„ ์ด์šฉํ•˜๋ฉด DBMS์ข…๋ฅ˜(MY SQL, ORACLE, MS SQL)๊ณผ ๊ด€๊ณ„์—†์ด ์ผ๊ด€๋œ ์ž๋ฐ”์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ํ”„๋กœ๊ทธ๋žจ ์œ ์ง€ ๋ฐ ๋ณด์ˆ˜๊ฐ€ ํŽธ๋ฆฌํ•˜๋‹ค.
  • "DBMS๋ž€? ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด"
  • ์ฟผ๋ฆฌ๋ฌธ์ด ํ†ต์ผ๋˜๊ธฐ์— ์˜ค๋ฅ˜ ๋ฐœ์ƒ๋ฅ ์„ ์ค„์—ฌ์ค€๋‹ค.

 

 

JPA(Java Persistence API)

  • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” JPA๋ฅผ ORM๊ธฐ์ˆ ์˜ ํ‘œ์ค€์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ์Œ์œผ๋กœ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์‹ค์ œ ํด๋ž˜์Šค๊ฐ€ ํ•„์š”ํ•˜๋‹ค. (ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ๋Œ€ํ‘œ์ ์ด๋‹ค.)
  • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” JPA์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์‹ค์ œ ํด๋ž˜์Šค์ด์ž ์ž๋ฐ”์˜ ORM ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ฆฌ๋ฅผ ๋„์™€์ค€๋‹ค.

 

H2 ์‚ฌ์šฉํ•˜๊ธฐ

H2๋ž€? ์ž๋ฐ” ๊ธฐ๋ฐ˜์˜ ๊ฒฝ๋Ÿ‰ DBMS๋‹ค.

 

H2๋ฅผ ์„ค์น˜ ํ›„ application.properties์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž…๋ ฅํ•œ๋‹ค.

# DATABASE
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:~/local
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

 

  • spring.h2.console.enabled=true : H2์ฝ˜์†”์— ์ ‘์†ํ• ๊ฒƒ์ธ์ง€ ๋ฌป๋Š” ํ•ญ๋ชฉ
  • spring.h2.console.path=/h2-console : H2์ฝ˜์†”๋กœ ์ ‘์†ํ•˜๊ธฐ ์œ„ํ•œ URL๊ฒฝ๋กœ
  • spring.datasource.url=jdbc:h2:~/local : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘์†ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒฝ๋กœ
  • spring.datasource.driverClassName=org.h2.Driver : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘์†ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋“œ๋ผ์ด๋ฒ„ ํด๋ž˜์Šค๋ช…
  • spring.datasource.username=sa ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์˜ ์‚ฌ์šฉ์ž๋ช… (๊ธฐ๋ณธ๊ฐ’์ด sa๋‹ค)
  • spring.datasource.password= ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ

 

 

url์— ์ ํžŒ ๊ฒฝ๋กœ์— local.mv.dbํŒŒ์ผ์„ ์ƒ์„ฑ ํ›„ (mac ๋ช…๋ น์–ด : touch local.mv.db)

http://localhost:8080/h2-console์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด

H2 ์ฝ˜์†” ํ™”๋ฉด์ด ๋œฌ๋‹ค.

JDBC URL์„ application.properties์— ์ ์—ˆ๋˜๋Œ€๋กœ ์ˆ˜์ •ํ•ด์ฃผ๊ณ  ์—ฐ๊ฒฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ๋‹ค.

 

์ž˜ ๋“ค์–ด์™”๋‹ค.

 

 

 

JPA ํ™˜๊ฒฝ ์„ค์ •

 

build.gradle dependencies์— ์ถ”๊ฐ€ํ•œ๋‹ค

implementation 'ord.springframework.boot:spring-boot-starter-data-jpa'

 

 

application.properties์— ์ถ”๊ฐ€ํ•œ๋‹ค

#JPA
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update

 

  • spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect : ์Šคํ”„๋ง๋ถ€ํŠธ์™€ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ• ๋•Œ ํ•„์š”ํ•œ ์„ค์ •
  • spring.jpa.hibernate.ddl-auto=update : ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ์˜ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๋Š” ๊ทœ์น™์„ ์„ค์ • 
    • update= ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ์šฉํ•œ๋‹ค.
    • create=์Šคํ”„๋ง๋ถ€ํŠธ๋ฅผ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ• ๋•Œ ํ…Œ์ด๋ธ”์„ ๋ชจ๋‘ ์‚ญ์ œํ•œ ํ›„ ๋‹ค์‹œ ์ƒ์„ฑํ•œ๋‹ค. (๋ฐ์ดํ„ฐ ๋‚ ๋ผ๊ฐˆ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์กฐ์‹ฌ)

 

 

์—”ํ‹ฐํ‹ฐ ๋งŒ๋“ค๊ธฐ

"์—”ํ‹ฐํ‹ฐ(entity)๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋˜๋Š” ์ž๋ฐ” ํด๋ž˜์Šค"

์˜ˆ) ๊ฒŒ์‹œํŒ ์„œ๋น„์Šค์—๋Š” ์งˆ๋ฌธ๊ณผ ๋‹ต๋ณ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ ์งˆ๋ฌธ๊ณผ ๋‹ต๋ณ€ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์žˆ์–ด์•ผํ•œ๋‹ค.

 

์งˆ๋ฌธ ๋ชฉ๋ก ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž

@Getter
@Setter
@Entity
public class Question {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	@Column(length = 200)
	private String subject;
	
	@Column(columnDefinition = "TEXT")
	private String content;
	
	private LocalDateTime createDate;
}

 

  • @Entity : ๋งŒ๋“  ํด๋ž˜์Šค๋ฅผ ์—”ํ‹ฐํ‹ฐ๋กœ ์ธ์‹
  • @Id : ๊ธฐ๋ณธํ‚ค
  • @GeneratedValue : ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ํ•ด๋‹น ์†์„ฑ์— ๊ฐ’์„ ์ผ์ผํžˆ ์ž…๋ ฅํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ 1์”ฉ ์ฆ๊ฐ€ ๋œ๋‹ค (=Auto Increment ๊ฐ™์€๊ฑฐ)
  • @Column : ์—ด์˜ ์„ธ๋ถ€ ์„ค์ • 
    • length : ์—ด์˜ ๊ธธ์ด
    • columnDefinition : ํ…์ŠคํŠธ๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค. ๊ธ€์ž ์ˆ˜ ์ œํ•œ X

 

 

๋‹ต๋ณ€ ํด๋ž˜์Šค๋„ ๋˜‘๊ฐ™์ด ๋งŒ๋“ค์–ด์ฃผ๊ณ  ํ•˜๋‚˜๋ฅผ ์ถ”๊ฐ€ ์‹œ์ผœ์คฌ๋‹ค.

@ManyToOne
	private Question question;

@ManyToOne : N:1๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

ํ•˜๋‚˜์˜ ์งˆ๋ฌธ์— ์—ฌ๋Ÿฌ ๋‹ต๋ณ€์ด ๋‹ฌ๋ฆด ์ˆ˜ ์žˆ๋‹ค (๋ถ€๋ชจ Question : ์ž์‹ Answer๊ด€๊ณ„)

๋‹ต๋ณ€ ์—”ํ‹ฐํ‹ฐ์˜ question์†์„ฑ๊ณผ ์งˆ๋ฌธ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์„œ๋กœ ์—ฐ๊ฒฐ๋œ๋‹ค (=์™œ๋ž˜ํ‚ค ๊ด€๊ณ„)

 

 

๋ฐ˜๋Œ€๋„ ๊ฐ€๋Šฅํ•˜๋‹ค

์งˆ๋ฌธ ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

@OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
	private List<Answer> answerList;

cascade = CascadeType.REMOVE : ์งˆ๋ฌธ์„ ์‚ญ์ œํ•˜๋ฉด ๋‹ต๋ณ€๋“ค๋„ ์‚ญ์ œ๋œ๋‹ค.

 

 

์ €์žฅ ํ›„ ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•˜๋ฉด

์ฝ˜์†”์— ๋งŒ๋“ค์–ด์ง„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ ์ƒ์„ฑํ•˜๊ธฐ

โ‹† ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๋“ค์„ ์ €์žฅ, ์กฐํšŒ, ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.

โ‹† ํ…Œ์ด๋ธ”์— ์ ‘๊ทผํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 

์งˆ๋ฌธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.

import org.springframework.data.jpa.repository.JpaRepository;

public interface QuestionRepository extends JpaRepository<Question, Integer> {

}

JpaRepository๋Š” JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์ค‘ ํ•˜๋‚˜๋กœ CRUD์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์„œ๋“ค์„ ๋‚ด์žฅํ•˜๊ณ  ์žˆ๋‹ค.

<Question, Integer>๋Š” Question ์—”ํ‹ฐํ‹ฐ๋กœ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๊ณ , ๊ธฐ๋ณธํ‚ค๊ฐ€ Integer๋ผ๋Š” ๊ฑธ ์•Œ๋ ค์ค€๋‹ค

 

 

 

JUnit ์„ค์น˜ํ•˜๊ธฐ๊ณ  ํ…Œ์ŠคํŠธํ•ด๋ณด๊ธฐ

๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•จ

 

build.gradle-dependencies์— ์ถ”๊ฐ€

testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

 

 

test/main/java - SbbApplicationTestsํด๋ž˜์Šค์—์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ดค๋‹ค.

@SpringBootTest
class SbbApplicationTests {
	
	@Autowired
	private QuestionRepository questionRepository;

	@Test
	void testJpa() {
		Question q1 = new Question();
		q1.setSubject("์Šคํ”„๋ง ๋ถ€ํŠธ ๊ณต๋ถ€๋Š” ์žฌ๋ฏธ์žˆ๋‚˜์š”?");
		q1.setContent("์Šคํ”„๋ง ๊ณต๋ถ€๋ฅผ ์‹œ์ž‘ํ•˜๋ ค๋Š”๋ฐ ๊ถ๊ธˆํ•ด์š”");
		q1.setCreateDate(LocalDateTime.now());
		this.questionRepository.save(q1);
		
		Question q2 = new Question();
		q2.setSubject("์–ผ๋งˆ๋‚˜ ๊ณต๋ถ€ํ•ด์•ผ ๊ฐœ๋ฐœ์ž๋กœ ์ทจ์—…ํ•  ์ˆ˜ ์žˆ๋‚˜์š”?");
		q2.setContent("๋งค์ผ ๊ณต๋ถ€ํ•˜๋Š”๋ฐ๋„ ๋ถˆ์•ˆํ•˜๋„ค์š” ์–ธ์ œ์ฏค ๋ ๋Ÿฐ์ง€..");
		q2.setCreateDate(LocalDateTime.now());
		this.questionRepository.save(q2);
		
	}

}

์„ค๋ช… ์ƒ๋žต

 

 

 

์ €์žฅ ํ›„ ์ฝ˜์†”์—์„œ select * from question์„ ์ž…๋ ฅํ•˜๋ฉด

์ด๋ ‡๊ฒŒ ์ž˜ ๋“ฑ๋ก ๋˜์—ˆ๋‹ค.

 

 

์กฐํšŒ, ์ˆ˜์ •, ์‚ญ์ œ๋„ ํ…Œ์ŠคํŠธ ์™„๋ฃŒ