[spring boot + security oauth] 세팅 + 테스트

개요

먼저 OAuth의 개념에 관한 자료는 http://helloworld.naver.com/helloworld/textyle/24942http://earlybird.kr/1584 를 참고하길 바란다. 이 글에서는 개념보다는 Provider쪽 세팅 예제와 테스트를 해보겠다. 테스트는 client_credentials, password, authorization_code 인증 방식을 사용하겠다.

참고 : 예제에서 curl 을 사용하기 때문에 윈도우용 curl을 설치 하시거나 cygwin 혹은 git bash 에서 curl을 사용하셔도 된다.

예제

먼저 STS 플러그인에 있는 Spring Starter Project로 프로젝트를 생성하자.

Web과 Security를 체크하고 생성한 다음, spring-security-oauth2 라이브러리를 추가한다. 그러면 pom.xml은 다음과 같다.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tistory.jekalmin</groupId>
    <artifactId>boot_oauth</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>boot_oauth</name>
    <description></description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.0.3.RELEASE</version>
        </dependency>
    </dependencies>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>com.tistory.jekalmin.Application</start-class>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

다음 Application.java를 아래와 같이 수정한다.


Application.java

package com.tistory.jekalmin;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Configuration
@ComponentScan
@EnableAutoConfiguration
@RestController
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RequestMapping("/")
    public String home() {
        return "Hello World";
    }

    @Configuration
    @EnableResourceServer
    protected static class ResourceServer extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                // Just for laughs, apply OAuth protection to only 2 resources
                .requestMatchers().antMatchers("/","/admin/beans").and()
                .authorizeRequests()
                .anyRequest().access("#oauth2.hasScope('read')");
            // @formatter:on
        }

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.resourceId("sparklr");
        }

    }

    @Configuration
    @EnableAuthorizationServer
    protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager);
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // @formatter:off
            clients.inMemory()
                .withClient("my-trusted-client")
                    .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
                    .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                    .scopes("read", "write", "trust")
                    .resourceIds("sparklr")
                    .accessTokenValiditySeconds(60)
            .and()
                .withClient("my-client-with-registered-redirect")
                    .authorizedGrantTypes("authorization_code")
                    .authorities("ROLE_CLIENT")
                    .scopes("read", "trust")
                    .resourceIds("sparklr")
                    .redirectUris("http://localhost:8080")
            .and()
                .withClient("my-client-with-secret")
                    .authorizedGrantTypes("client_credentials", "password")
                    .authorities("ROLE_CLIENT")
                    .scopes("read")
                    .resourceIds("sparklr")
                    .secret("secret");
        // @formatter:on
        }

    }

    @Configuration
    @EnableWebMvcSecurity
    protected static class SecurityConfig extends WebSecurityConfigurerAdapter{

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().withUser("min").password("min").roles("USER");
        }

        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean()
                throws Exception {
            return super.authenticationManagerBean();
        }

    }

}

이러면 서버쪽 세팅은 끝이다. 이제 AccessToken을 받아오자.



인증 방법

  • client_credentials

    my-client-with-secret 라는 클라이언트에 client_credentials 인증방법으로 인증해보자.

      curl -u my-client-with-secret:secret http://localhost:8080/oauth/token -d "grant_type=client_credentials"
    

    결과는 다음과 같았다.

      {"access_token":"4444455c-a8a5-4988-ae61-c4b15080e433","token_type":"bearer","expires_in":43200,"scope":"read"}
    



  • password

    이번엔 my-trusted-client 클라이언트에 password 방법으로 인증해보자.

      curl -u my-trusted-client: http://localhost:8080/oauth/token -d "grant_type=password&username=min&password=min"
    

    결과는 다음과 같다.

      {"access_token":"c09bfac0-274b-4dc6-a15c-ec46d2680d44","token_type":"bearer","refresh_token":"9b5c996c-eede-4111-b3d1-aaff28e5c063","expires_in":49,"scope":"read write trust"}
    



  • authorization_code

    my-client-with-registered-redirect 클라이언트에 authorization_code 방법으로 인증하자.
    이전과는 다르게 AccessToken을 발급 받기 위해서는 권한 승인해서 코드를 가져오는 과정이 필요하다. http://localhost:8080/oauth/authorize?client_id=my-client-with-registered-redirect&response_type=code 로 가서 먼저 등록된 min / min 계정으로 로그인 하면 다음과 같은 창이 뜬다.

    둘 다 허용하고 승인하면 아래 화면으로 이동한다.

    보시는 바와 같이 http://localhost:8080/?code=h6cdAM 로 리다이렉트 된다. 이제 code를 이용해서 AccessToken을 발급 받아보자.

      curl -u my-client-with-registered-redirect: http://localhost:8080/oauth/token -d "grant_type=authorization_code&code=h6cdAM"
    

    결과는 다음과 같았다.

      {"access_token":"1ce32b00-05fa-40f2-a1f5-f6ed2719df4e","token_type":"bearer","expires_in":43199,"scope":"trust read"}
    

어떤 방법이든 인증에 성공했으면, AccessToken을 받급 받았을 것이다. 토큰을 가지고 테스트 하기에 앞서 Application.java에서 설정했던 부분을 다시 살펴보자.

@RequestMapping("/")
public String home() {
    return "Hello World";
}

접근에 성공하면 Hello World가 출력되야 한다.
이제 http://localhost:8080 에 접근해보자. 먼저 토큰을 사용하지 않고 접근해서 보호된 자원임을 확인하자.

C:\Users\Min>curl http://localhost:8080
{"error":"unauthorized","error_description":"An Authentication object was not found in the SecurityContext"}

접근이 안된다. 이제 위에서 받았던 AccessToken을 사용해서 접근해보자.

C:\Users\Min>curl -H "authorization: bearer 1ce32b00-05fa-40f2-a1f5-f6ed2719df4e" http://localhost:8080
Hello World

AccessToken을 가지고 요청할 때에만 접근이 되는 것을 확인할 수 있다.

결론

인증 방법에 따라 파라미터로 넘겨야 하는 필수 항목들이 있는데, 최소한의 파라미터로 테스트를 했다. 필수 파라미터들은 http://tutorials.jenkov.com/oauth2/authorization-code-request-response.html 에서 확인할 수 있다.

spring boot를 사용해서 spring security oauth 설정해서 간단해 보이지만, xml 설정을 해보면 TokenEndPoint, TokenStore, Filter 등 설정해야 할 부분이 많다. 그렇게 설정을 해도 결과가 좋지 않았다. 나중에 xml 기반 세팅도 완료 되는데로 포스팅 하겠다.

ps : 예제는 https://github.com/dsyer/sparklr-boot 소스에서 security 세팅을 추가했다.

참고

spring boot + spring-data-jpa 설정

개요

이전 글인 (http://jekalmin.tistory.com/entry/springdatajpa-%EA%B8%B0%EB%B3%B8-%EC%98%88%EC%A0%9C) 에서 봤듯이 spring-data-jpa 를 설정하려면 xml에 상당히 많은 설정이 필요한 것을 볼 수 있다. 이런 많은 설정들이 spring boot를 사용하면서 기본으로 많이 제공해준다. 예제를 보면서 얼마나 간편해졌는지 확인해보자.

예제

먼저 eclipse를 쓰고 있다면 STS(Spring Tool Suite) 플러그인을 설치하자. 설치하고 나면 New > Project > Spring Starter Project 로 프로젝트를 생성하자.

생성할 때 스타일을 사용할 것인지 선택할 수 있다. 예제에서는 JPA와 Web만 선택하겠다.

먼저 hsqldb 라이브러리를 추가하자.

<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
</dependency>

자, 이제 설정 준비가 끝났다. 클래스를 작성해보자.


Member.java

package com.tistory.jekalmin.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Member {

    @Id
    @GeneratedValue
    private int memberSeq;
    private String name;
    private int age;

    /**
     * 다른 생성자를 만들었다면 기본 생성자를 따로 만들어 주는 것을 잊지말자.
     */
    public Member(){}

    public Member(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public int getMemberSeq() {
        return memberSeq;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Member [memberSeq=" + memberSeq + ", name=" + name + ", age="
                + age + "]";
    }

}


MemberRepository.java

package com.tistory.jekalmin.repository;

import org.springframework.data.repository.CrudRepository;

import com.tistory.jekalmin.domain.Member;

public interface MemberRepository extends CrudRepository<Member, Integer>{

}


MemberController.java

package com.tistory.jekalmin.controller;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.tistory.jekalmin.domain.Member;
import com.tistory.jekalmin.repository.MemberRepository;

@Controller
public class MemberController {

    @Resource
    private MemberRepository repository;

    @RequestMapping(value="/member/join")
    public void join(Member member){
        System.out.println( repository.save(member) );
    }

}


save 하고나서 저장된 객체를 리턴해주기 때문에 바로 print 찍어보았다.

http://localhost:8080/member/join?name=min&age=26 요청을 해본 결과는 다음과 같았다.

Member [memberSeq=1, name=min, age=26]

결론

이전의 어마어마한 세팅들이 다 어디갔나 싶을 정도로 boot에서는 기본 설정이 많이 내장되어 있는 것 같다. boot를 이용해서 만들면 설정은 hsqldb 라이브러리 추가하는 것이 전부였고, entity와 repository를 만들어서 바로 사용하니 된다.

설정을 변경할 필요가 있다면 지원하는 어노테이션을 검색해 봐야 겠지만, 기본 설정하나는 정말 쉬워진 것 같다.

[Java] Http 요청 보내기 (HttpClient)

개요

자바스크립트에서는 태그에 넣으면 host, pathname, protocol, port, search 등을 지원해서 url 관리하기가 편리하다.

하지만 자바에서는 Url을 핸들링하기가 쉽지않다. 그래서 아파치에서 제공하는 유틸을 소개하려 한다.

예제

  • Uri 생성하기

    먼저 http 요청을 하게 해주는 라이브러리를 추가해야 한다.

      <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpclient</artifactId>
          <version>4.3.5</version>
      </dependency>
    

    그리고 아래와 같이 코드를 작성해준다.

    HttpTest.java

      package com.tistory.jekalmin;
    
      import java.io.IOException;
      import java.net.URI;
      import java.net.URISyntaxException;
    
      import org.apache.http.client.ClientProtocolException;
      import org.apache.http.client.utils.URIBuilder;
    
      public class HttpTest {
    
          public static void main(String[] args) throws URISyntaxException, ClientProtocolException, IOException {
    
              URI uri = new URI("http://jekalmin.tistory.com");
              uri = new URIBuilder(uri).addParameter("aaa", "bbb").addParameter("ccc", "ddd").build();
              System.out.println(uri);
    
          }
    
      }
    

    URIBuilder 클래스를 사용해서 처음부터 host, protocol, port 까지 정해주며 생성할 수도 있고, 예제와 같이 파라미터만 추가도 가능하다. 결과는 다음과 같다.

      http://jekalmin.tistory.com?aaa=bbb&ccc=ddd
    
  • http 요청하기

    이제 생성한 uri를 가지고 http 요청을 해보자. 아래와 같이 코드를 추가해준다.

    HttpTest.java

      package cpackage com.tistory.jekalmin;
    
      import java.io.IOException;
      import java.net.URI;
      import java.net.URISyntaxException;
    
      import org.apache.http.HttpEntity;
      import org.apache.http.HttpResponse;
      import org.apache.http.client.ClientProtocolException;
      import org.apache.http.client.HttpClient;
      import org.apache.http.client.methods.HttpGet;
      import org.apache.http.client.utils.URIBuilder;
      import org.apache.http.impl.client.HttpClientBuilder;
      import org.apache.http.util.EntityUtils;
    
      public class HttpTest {
    
          public static void main(String[] args) throws URISyntaxException, ClientProtocolException, IOException {
    
              URI uri = new URI("http://jekalmin.tistory.com");
              uri = new URIBuilder(uri).addParameter("aaa", "bbb").addParameter("ccc", "ddd").build();
    
              HttpClient httpClient = HttpClientBuilder.create().build();
              HttpResponse response = httpClient.execute(new HttpGet(uri)); // post 요청은 HttpPost()를 사용하면 된다. 
              HttpEntity entity = response.getEntity();
              String content = EntityUtils.toString(entity);
              System.out.println(content);
    
          }
    
      }
    

    4.0부터는 DefaultHttpClient 클래스를 사용했으나 4.3부터는 deprecated 된 상태이다. 대신 HttpClientBuilder를 사용하여 생성하기를 권장하고 있다.
    http 요청후 내용 받아오는 부분은 원래 entity.getContent() 인데 getContent()는 InputStream을 반환한다. 바로 String으로 받기 위해 EntityUtils를 사용했다.

  • Uri에서 파라미터 파싱하기

    host, port, path 등의 정보는 URI 객체에서 getHost() 등과 같은 메소드로 바로 가져올 수 있다. 하지만 파라미터의 경우는 제공하지 않는다.
    그래서 아파치에서 제공하는 유틸을 사용했다. 아래 예제를 보자.

    HttpTest.java

      package com.tistory.jekalmin;
    
      import java.io.IOException;
      import java.net.URI;
      import java.net.URISyntaxException;
      import java.util.List;
    
      import org.apache.http.NameValuePair;
      import org.apache.http.client.ClientProtocolException;
      import org.apache.http.client.utils.URIBuilder;
      import org.apache.http.client.utils.URLEncodedUtils;
    
      public class HttpTest {
    
          public static void main(String[] args) throws URISyntaxException, ClientProtocolException, IOException {
    
              URI uri = new URI("http://jekalmin.tistory.com");
              uri = new URIBuilder(uri).addParameter("aaa", "bbb").addParameter("ccc", "ddd").build();
              System.out.println(uri);
    
              List<NameValuePair> paramList = URLEncodedUtils.parse(uri, "utf-8");
              for(NameValuePair param : paramList){
                  System.out.println(param.getName() + " = " + param.getValue());
              }
          }
    
      }
    

    결과는 아래와 같다.

      http://jekalmin.tistory.com?aaa=bbb&ccc=ddd
      aaa = bbb
      ccc = ddd
    

    아파치에서는 URLEncodedUtils 라는 클래스를 제공한다. 위의 parse 외에도 거꾸로 변환해주는 format 기능도 있다.
    두 메소드는 오버로딩을 통해 다양한 파라미터 타입을 제공하기도 한다.

결론

예전에는 자바에서 http 요청을 보낼 때, string을 결합하여 uri를 직접 생성하거나 그 작업을 도와주는 유틸을 만들곤 했었는데, 이미 다 제공되고 있었다. EntityUtil과 URLEncodedUtils는 4.0 버전부터 지원한다.
코딩하다가 불편하다고 느낄때 무작정 유틸을 만드는 것은 많은 테스트를 안하면 안전하지 않을 뿐만 아니라 많은 시간이 소요된다. 내가 불편함을 느낀다면, 다른 사람들도 똑같이 느꼈을 것이다. 직접 만드는 것은 최후의 보루로 남겨두자.

[spring-data-jpa] 기본 예제

소개

spring-data-jpa는 jpa 기반 저장소를 쉽게 구현하도록 만들어준다. 이중에서도 눈에 띄는 특징들은

  • type-safe한 쿼리가 가능하다.
  • 공통된 저장소 기능들(CRUD)을 제공해준다.
  • @Query 어노테이션을 제공한다.

그 외에도 querydsl의 predicate 제공이나 xml 기반의 entity 매핑등의 특징들도 보인다.

예제

먼저 Spring Project로 프로젝트를 생성하고 dependency를 추가하자.

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>1.7.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>2.3.2</version>
</dependency>


스프링 프로젝트가 기본 dependency를 등록해주므로 우리는 두개만 더 등록해주자.

다음 Article.java 라는 엔티티를 만들어보자.


Article.java

package com.tistory.jekalmin.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Article {

    @Id        // primary key
    @GeneratedValue        // auto increment
    private int articleNo;
    private int memberNo;
    private String title;
    private String content;
    /* 컬럼명은 필드명이랑 동일하게 된다. 따로 명시해주고 싶으면 @Column을 사용하자 */

    /**
     * 하이버네이트가 파라미터 없는 생성자를 호출하기 때문에, 파라미터 있는 생성자를 생성했을 경우
     * 꼭 파라미터 없는 생성자를 만들어 줘야 한다.
     */
    public Article(){}

    public Article(int memberNo, String title, String content) {
        this.memberNo = memberNo;
        this.title = title;
        this.content = content;
    }

    public int getArticleNo() {
        return articleNo;
    }
    @SuppressWarnings("unused")
    private void setArticleNo(int articleNo) {
        this.articleNo = articleNo;
    }
    public int getMemberNo() {
        return memberNo;
    }
    public void setMemberNo(int memberNo) {
        this.memberNo = memberNo;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Article [articleNo=" + articleNo + ", memberNo=" + memberNo
                + ", title=" + title + ", content=" + content + "]";
    }


}


그리고 Article 엔티티의 저장소를 만든다.


ArticleRepository.java

package com.tistory.jekalmin.repository;

import org.springframework.data.repository.CrudRepository;

import com.tistory.jekalmin.domain.Article;

public interface ArticleRepository extends CrudRepository<Article, Integer> {

}


이제 applicationContext 설정만 하면 된다.


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <context:component-scan base-package="com.tistory.jekalmin"/>
    <context:annotation-config/>

    <jdbc:embedded-database id="dataSource" type="HSQL"></jdbc:embedded-database>

    <!-- spring data jpa 설정 -->

    <!-- jpa repository가 위치한 패키지 경로 등록 -->
    <jpa:repositories base-package="com.tistory.jekalmin.repository" entity-manager-factory-ref="entityManagerFactory"/>

    <!-- 하이버네이트의 SessionFactory 에 상응하는 jpa의 EntityManagerFactory 등록 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> <!-- Hsql 형식에 맞게 변환해주는 클래스 -->
                <prop key="hibernate.connection.pool_size">1</prop>
                <prop key="hibernate.connection.shutdown">true</prop> <!-- hsql에 있는 마지막 연결이 끊어지면 데이터베이스 shutdown 하는 플래그 -->
                <prop key="hibernate.show_sql">true</prop> <!-- SQL 출력 -->
                <prop key="hibernate.hbm2ddl.auto">create</prop> <!-- 테이블 자동 생성 -->
            </props>
        </property>
        <!-- 엔티티 정의된 클래스들이 있는 패키지 등록 -->
        <property name="packagesToScan" value="com.tistory.jekalmin.domain"/>
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
    </bean>
    <!-- spring data jpa 설정 끝 -->



</beans>


설정 부분이 쉽지 않은데, 약간의 주석들이 도움이 되길 바란다.

이제 테스트할 클래스만 만들고 테스트하면 된다.


ArticleRepositoryTest.java

package com.tistory.jekalmin.test;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.tistory.jekalmin.domain.Article;
import com.tistory.jekalmin.repository.ArticleRepository;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/application-config.xml")
public class ArticleRepositoryTest {

    @Resource
    ArticleRepository articleRepository;

    @Test
    public void write(){

        articleRepository.save(new Article(11, "제목", "내용"));

        System.out.println(articleRepository.findAll());

    }


}


spring-data-jpa의 가장 강력한 기능중 하나가 Repository의 기본 기능을 제공하는 것 같다. 다만 인터페이스만 선언한 만큼, 나만의 메소드를 추가하는 방법이 조금 까다롭다. 메소드를 추가하는 방법은 다음에 작성하겠다.