Spring boot ๊ธฐ์ด ๋ฐ ์ค์
๋ณต์ต์ฉ
localhost:8080
localhost(=127.0.0.1) : ๋ด ์ปดํจํฐ IP ์ฃผ์
8080 : ํฌํธ ๋ฒํธ
์ ๋ํ ์ด์ (annotation)
@Controller : ์ปจํธ๋กค๋ฌ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ค.
@GetMapping("/hello") : localhost:8080/hello
@ResponseBody : ๋ฉ์๋์ ์ถ๋ ฅ ๊ฒฐ๊ณผ๊ฐ ๋ฌธ์์์ ๋ํ๋ธ๋ค.
@RequiredArgsConstructor : ํ์ํ ์์ฑ์๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด์ค๋ค. (@Setter ํ์ X)
ํ๋ก์ ํธ ๊ตฌ์กฐ
- 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์ ์ ๋ ฅํ๋ฉด
์ด๋ ๊ฒ ์ ๋ฑ๋ก ๋์๋ค.
์กฐํ, ์์ , ์ญ์ ๋ ํ ์คํธ ์๋ฃ