[๊ฐ์] ์คํ๋ง ์ ๋ฌธ - ์ฝ๋๋ก ๋ฐฐ์ฐ๋ ์คํ๋ง ๋ถํธ, ์น MVC, DB์ ๊ทผ ๊ธฐ์ 3
[๋ฌด๋ฃ] ์คํ๋ง ์ ๋ฌธ - ์ฝ๋๋ก ๋ฐฐ์ฐ๋ ์คํ๋ง ๋ถํธ, ์น MVC, DB ์ ๊ทผ ๊ธฐ์ - ์ธํ๋ฐ | ๊ฐ์
์คํ๋ง ์ ๋ฌธ์๊ฐ ์์ ๋ฅผ ๋ง๋ค์ด๊ฐ๋ฉด์ ์คํ๋ง ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ์ ๋ฐ์ ๋น ๋ฅด๊ฒ ํ์ตํ ์ ์์ต๋๋ค., ์คํ๋ง ํ์ต ์ฒซ ๊ธธ์ก์ด! ๊ฐ๋ฐ ๊ณต๋ถ์ ๊ธธ์ ์์ง ์๋๋ก ๋์๋๋ฆฝ๋๋ค. ๐ฃ ํ์ธํด์ฃผ์ธ
www.inflearn.com
์ถ์ฒ : inf.run/reCZ ๊ฐ์
6.์คํ๋ง DB ์ ๊ทผ ๊ธฐ์
- H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์น
์ฐ๊ฒฐํด๋ณด๊ณ
insert into member(name) values('spring') - "์๋๊ณ '์ธ๊ฑฐ ์ฃผ์, ์ด๊ฑธ๋ก ์ด๋ฆ์ ๋ฃ์ด ๋ณด์๋ค
- ์์ JDBC
์์ ์ ์ฌ์ฉ - ์ฐธ๊ณ ๋ง ํ๊ณ ๋์ด๊ฐ์
๊ธฐ์กด์ ์ฝ๋๋ ํ๋๋ ์๋์ง ์๊ณ SpringConfig.java๋ง ์๋์ ๊ฐ์๋ผ์ธ ์ ์๋ค.
๊ฐ๋ฐฉ-ํจ์ ์์น : ํ์ฅ์๋ ์ด๋ ค์๊ณ , ์์ , ๋ณ๊ฒฝ์๋ ๋ซํ์๋ค.
์ถ๊ฐ ์ฝ๋
build.gradle์ ์ถ๊ฐ
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
application.properties์ ์ถ๊ฐ
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
//๊ณต๋ฐฑ ๋ชจ๋ ์ ๊ฑฐ!!
JdbcMemberRepository.java
- ์คํ๋ง ํตํฉ ํ ์คํธ
@SpringBootTest : ์คํ๋ง ์ปจํ ์ด๋์ ํ ์คํธ ํจ๊ป ์คํ. ์ ๋ง spring์ด ๋์๊ฐ
์์ ์๋ฐ ์ฝ๋ ๋จ์ ํ ์คํธ vs ํตํฉ ํ ์คํธ
์ ์๊ฐ ์ข์ ํ๋ฅ ์ด ๋๋ค. ๋น ๋ฅด๊ธฐ ๋๋ฌธ ์ฌ๋งํ๋ฉด ์ ์๋ก ๊ฐ์
@Transactional : ํ ์คํธ ์์ ์ ์ ํธ๋ ์ ์ ์์, ์๋ฃ ํ ๋กค๋ฐฑ. DB์ ๋จ์ง ์๋๋ค.
//MemberServiceIntegrationTest.java
@SpringBootTest
@Transactional //->์์ผ๋ฉด DB์ ์์ธ๋ค(๋ฐ๋ณต๋ถ๊ฐ)
public class MemberServiceIntegrationTest {
//์ง์ ๊ฐ์ฒด ๋ง๋ค๊ธฐ -> contructor ๊ฐ์ง๊ณ ํ๊ธฐ
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
//AfterEach๋ ํ์ x
@Test
void ํ์๊ฐ์
() { //ํ๊ธ๋ก ํด๋ ๋๋ค!
//given ์ํฉ์ด ์ฃผ์ด์ง
Member member = new Member();
member.setName("spring");
//when ์ด๊ฑธ๋ก ์คํํจ
Long saveId = memberService.join(member);
//then ์ด๋ฐ ๊ฒฐ๊ณผ๊ฐ ๋์ด
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
}
//์คํ ํ
์คํธ์ธ๋ฐ๋ spring์ด ๋ฌ๋ค!
@Test
public void ์ค๋ณต_ํ์_์์ธ(){
//given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member1.setName("spring");
//when
memberService.join(member1);
/*1)
try {
memberService.join(member2);
fail(); //fail์์ ๊ณ์ ์ค๋ฅ๋จ ์ด์ ์์๋ผ ๊ฒ
}catch(IllegalStateException e){
assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
}*/
//2)
IllegalStateException e = assertThrows(IllegalStateException.class,
() -> memberService.join(member1));
//then
}
}
- ์คํ๋ง JDBCTemplate
๋ฐ๋ณต ์ฝ๋๋ฅผ ์ ๊ฑฐํด์ค๋ค.
//JdbcTemplateMemberRepository.java
public class JdbcTemplateMemberRepository implements MemberRepository{
private final JdbcTemplate jdbcTemplate;
//@Autowired ์์ฑ์๊ฐ ํ๋์ผ ๊ฒฝ์ฐ ์๋ต ๊ฐ๋ฅ
public JdbcTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
HashMap<String, Object> parameters = new HashMap<>();//!!!
parameters.put("name", member.getName());
Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
member.setId(key.longValue());
return member;
}
@Override
public Optional<Member> findById(Long id) {
List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id);
return result.stream().findAny();
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
private RowMapper<Member> memberRowMapper() {
return (rs, rowNum) -> { //new RowMapper<Member>()
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
์ฝ๋๊ฐ ์์ jdbc๋ณด๋ค ํจ์ฌ ๊ฐ๊ฒฐํด์ง ๊ฒ์ ์ ์ ์๋ค.
์๊น ๋ง๋ MemberServiceIntegrationTest.java๋ก ํ ์คํธ๋ ํด๋ณด์! ๋ ์๋๋ค.
- JPA
๊ฐ์ฒด๋ฅผ ๋ฐ๋ก DB์ Query์์ด ์ ์ฅ, ๊ด๋ฆฌ ๊ฐ๋ฅ
JDBCํ ํ๋ฆฟ์ผ๋ก ์ฝ๋๊ฐ ๋งค์ฐ ๊ฐ๊ฒฐํด์ก์ง๋ง ๊ฒฐ๊ตญ SQL์ ์ง์ ์ง์ผํ๋ค -> ์ด ๋ฌธ์ ํด๊ฒฐ JPA ์์ฐ์ฑ ์ !
์ถ๊ฐ ์ฝ๋
build.gradle์ ์ถ๊ฐ
//implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
application.properties์ ์ถ๊ฐ
spring.jpa.show-sql=ture
spring.jpa.hibernate.ddl-auto=none
//domain.Member.java์ @์ถ๊ฐ
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;//id ์๋ณ์, ์์คํ
์ด ์๋ ์ ์ฅ
//๋ง์ฝ DB ๋ณ์๋ช
์ด name์ด ์๋๋ผ๋ฉด, @Column(name = "username") ์ผ๋ก ๋งตํ
private String name;//์ด๋ฆ
...
}
//JpaMemberRepository.java
public class JpaMemberRepository implements MemberRepository{
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member save(Member member) {
em.persist(member); //์ฌ๊ธฐ์ ๋ชจ๋ ๊ฑธ ๋คํด์ค
return member;
}
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class).getResultList();
//Member entity๋ฅผ ์๋๋ก Query๋ฅผ ๋ ๋ฆผ
}
}
์ญ์ MemberServiceIntegrationTest.java๋ก ํ ์คํธ ๋๋ ค๋ดค๋๋ฐ ์๋จ.
- ์คํ๋ง ๋ฐ์ดํฐ JPA
JPA๋ฅผ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ๋๋ก ํ ๋ฒ ๊ฐ์ผ ๊ธฐ์
interface๋ง์ผ๋ก ๊ฐ๋ฐ์ ์๋ฃํ ์ ์๋ค.
//SpringDataJavaMemberRepository.java
public interface SpringDataJavaMemberRepository
extends JpaRepository<Member,Long>/*๋ง์ ๋ฉ์๋ ์ ๊ณต ๊ฐ์ ธ๋ค์ฐ๊ธฐ*/, MemberRepository {
@Override
Optional<Member> findByName(String name);
}
//SpringConfig.java
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
@Autowired
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
}
7.AOP
- AOP๊ฐ ํ์ํ ์ํฉ
//MemberService.java
@Transactional
public class MemberService {
...
//ํ์๊ฐ์
public Long join(Member member){
long start = System.currentTimeMillis();
try {
//๊ฐ์ ์ด๋ฆ ์ค๋ณตํ์ x
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("join = " + timeMs + "ms");
}
}
...
//์ ์ฒด ํ์ ์กฐํ
public List<Member> findMembers(){
long start = System.currentTimeMillis();
try {
return memberRepository.findAll();
}finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("findMembers " + timeMs + "ms");
}
}
...
}
ํ์ง๋ง ํต์ฌ ๋ก์ง๊ณผ ๊ณตํต ๊ด์ฌ ์ฌํญ(์๊ฐ ์ธก์ ) ์ด ์์ฌ์์ด ์ ์ง ๋ณด์๊ฐ ์ด๋ ต๋ค.
- AOP ์ ์ฉ
์๊น ๋ง๋ ์ง์ ์๊ฐ์ ํ๋ฆฐํธ ํ๋ ์ฝ๋๋ ์ง์ฐ๊ณ ํต์ฌ ๋ก์ง๋ง ๋จ๊ฒจ๋๋๋ค.
//TimeTraceAop.java
@Aspect
/*1)*/@Component
public class TimeTraceAop {
@Around("execution(* hello.hellospring..*(..))")
public Object excute(ProceedingJoinPoint joinPoint) throws Throwable{
long start = System.currentTimeMillis();
System.out.println("START : " + joinPoint.toString());
try{
return joinPoint.proceed();
}finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END : " + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
/*2) SpringConfig.java์ ์ถ๊ฐ
@Bean
public TimeTraceAop timeTraceAop(){
return new TimeTraceAop();
}*/
ํต์ฌ ๊ด์ฌ ์ฌํญ์ ๊น๋ํ๊ฒ ์ ์งํ ์ ์๋ค.
ํ๋ก์๋ผ๋ ๊ธฐ์ ๋ก ๋ฐ์ํ๋ ๊ฐ์ง MemberService๋ฅผ ๋ถ๋ฌ์จ๋ค. ์ฌ๊ธฐ์ AOP๊ฐ ์คํ๋๊ณ ๋์ค์ joinPoint.proceed();๊ฐ ์ง์ง ๋ฐ๋ ค์ด