-
[๊ฐ์] ์คํ๋ง ์ ๋ฌธ - ์ฝ๋๋ก ๋ฐฐ์ฐ๋ ์คํ๋ง ๋ถํธ, ์น MVC, DB์ ๊ทผ ๊ธฐ์ 3SPRING/INFLEARN 2021. 4. 5. 20:54
์คํ๋ง ๊ณต๋ถ~ [๋ฌด๋ฃ] ์คํ๋ง ์ ๋ฌธ - ์ฝ๋๋ก ๋ฐฐ์ฐ๋ ์คํ๋ง ๋ถํธ, ์น MVC, DB ์ ๊ทผ ๊ธฐ์ - ์ธํ๋ฐ | ๊ฐ์
์คํ๋ง ์ ๋ฌธ์๊ฐ ์์ ๋ฅผ ๋ง๋ค์ด๊ฐ๋ฉด์ ์คํ๋ง ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ์ ๋ฐ์ ๋น ๋ฅด๊ฒ ํ์ตํ ์ ์์ต๋๋ค., ์คํ๋ง ํ์ต ์ฒซ ๊ธธ์ก์ด! ๊ฐ๋ฐ ๊ณต๋ถ์ ๊ธธ์ ์์ง ์๋๋ก ๋์๋๋ฆฝ๋๋ค. ๐ฃ ํ์ธํด์ฃผ์ธ
www.inflearn.com
์ถ์ฒ : inf.run/reCZ ๊ฐ์
6.์คํ๋ง DB ์ ๊ทผ ๊ธฐ์
- H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์น
๋น๋ซ์ด์ง ์ฃผ์ ์๋๊ฑธ ๋ค์ด๋ฐ์ผ์ธ์ผ ์๊ด ์๋ค๊ณ ๋ ํ๋๋ฐ ๋ ์์๊ฑฐ ๋ฐ์ผ๋๊น ์๋๋๋ฐ ๋ฐ์ ํ์ผ์ ์์ถ์ ํ์ด์ ๋ฉ์ง ์์น์ ๋๊ณ bin\h2.bat์ ์คํํ๋ค. ./h2.sh๋ ์๋์ฐ๊ฑฐ๊ฐ ์๋๋ ์๋์ฐ๋ผ๋ฉด ๋ฐ๋ณด์ฒ๋ผ ์ ๋ ๊ฒ ํ์ง ์๋๋ก ์ฃผ์ํ๋ค. ํ๊ณ ๋๋ฉด ์๋ฐ ์ฐฝ์ด ๋ฌ๋ค ์ฐ๊ฒฐํด๋ณด๊ณ
cmd ๋ค์ด๊ฐ์๋ง์ dir๋ก ์ ๊ฑฐ ์๊ฒผ๋ ํ์ธ ์๋ ๊ฒฝ์ฐ์ ๋ ธ๋ ๋ถ๋ถ์ localhost๋ก ๋ณ๊ฒฝํ๋ค 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๋ฅผ ๋ ๋ฆผ } }
@Transactional ์ถ๊ฐ ์ญ์ 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();๊ฐ ์ง์ง ๋ฐ๋ ค์ด
'SPRING > INFLEARN' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ