SPRING/INFLEARN

[๊ฐ•์˜] ์Šคํ”„๋ง ์ž…๋ฌธ - ์ฝ”๋“œ๋กœ ๋ฐฐ์šฐ๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ, ์›น MVC, DB์ ‘๊ทผ ๊ธฐ์ˆ  3

ozllzL 2021. 4. 5. 20:54

์Šคํ”„๋ง ๊ณต๋ถ€~

inf.run/reCZ

 

[๋ฌด๋ฃŒ] ์Šคํ”„๋ง ์ž…๋ฌธ - ์ฝ”๋“œ๋กœ ๋ฐฐ์šฐ๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ, ์›น 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();๊ฐ€ ์ง„์งœ ๋ฐ๋ ค์˜ด