반응형

개요

Spring-data-jpa를 사용하면 Entity Manager 를 사용하지 않고 쉽게 구현 가능하지만 가끔 Entity Manager로 직접 영속성 컨텍스트를 컨트롤 해야할 필요가 생긴다. 어떤식으로 둘을 함께 사용할 수 있는지 헤딩한 내용을 기록해본다.

  • 목차
    • Spring-data-jpa 와 jpa의 차이
    • 함께 사용해보자
    • 정리

 

Spring-data-jpa 와 jpa의 차이


Spring-data-jpa  JPA를 추상화 시켜둔 개념으로, 개발자가 EntityManager로 직접 접근 없이도 쉽게 개발 할 수 있도록 도와주는 모듈이다. Spring-data-jpa를 구성하는 내부에 Entity Manager가 구현되어 있어 사용자는 Repository Interface를 정의하는 것 만으로 JPA를 손쉽게 사용할 수 있다.

spring-data-jpa Repository


흔히 사용하는 spring-data-jpa Repository 예시

@Repository
public interface workRepository {
	int insertNtisNotice(FeedMessage feedMessage) throws Exception;
	List<FeedMessage> findByMessageLikeOrderByIDDesc(String message) throws Exception;
	int insertNtisNoticeAuto(FeedMessage feedMessage) throws Exception;
	int deleteNtisNoticeAutoCheck() throws Exception;
	List<AccountInfoDTO> selectAccountInfo(AccountInfoDTO accountInfoDTO) throws Exception;
	AccountInfoDTO findByManagerByID(int id) throws Exception;
}

interface 만으로 쿼리에 접근하도록 돕는다. 이것만으로도 반복에 가까운 자바 코드가 줄어든다.

하지만 뭔가 부족하다. 어쩌다 한 두번씩 EntityManager를 사용해야 할 때가 온다.. 그럼 그냥 함께 사용해보자.

 

함께 사용해보자


SpringBoot 프로젝트로 간단하게 구현해보자.

1. 프로젝트 생성하기

스프링부트 프로젝트 만들기 에서 프로젝트를 만들거나, 또는 하단 예제처럼 이클립스에서 New Spring Starter Project 로 프로젝트를 만든다.

설정은 크게 상관없다. Gradle 이나 자바 버전을 8로 선택한 건 단지 내가 익숙해서다.

간단하게 구현 테스트만 해 볼 것이기 때문에 메모리타입 DB를 지원하는 H2 DB와 편의성이 엄청난 Lombok, 당연히 Spring Data Jpa 와 웹서비스를 구현하는 것이므로 Spring Web도 선택한다.

자주 사용하는 항목에 저게 없으면 하단 검색창에서 검색하여 선택하면 된다.

사실 제대로 선택 안해도 상관없다. 프로젝트가 생성된 후 gradle 파일을 직접 수정하고 Refresh gradle project 해주면 제대로 반영된다.

finish 하면 프로젝트가 생성된다.

2. build.gradle 파일

plugins {
	id 'java'
	id 'org.springframework.boot' version '2.4.5'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
	maven { url 'https://repo.spring.io/milestone' }
	maven { url 'https://repo.spring.io/snapshot' }
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

예제를 따라 했다면 위 내용은 버젼만 좀 다르고 거의 같을 것이다.

3. H2 DB 설정하기

spring:
  datasource: 
    url: jdbc:h2:mem:demo #메모리 타입DB로 demo라는 DB에 접근한다는 뜻.
    username: sa          #사용자는 필수 입력사항.
    password:             #패스워드는 선택사항.
    driver-class-name: org.h2.Driver
  h2:
    console:
      enabled: true # H2 DB의 웹 콘솔 사용여부 설정. (http://localhost:8080/h2-console 로 접근)
  jpa:
    database: H2
    show-sql: true # jpa 에서 쿼리 출력여부 설정
    properties:
      hibernate:
        format_sql: true # 쿼리 출력시 정렬 여부 설정 

src/main/resource 경로에 application.yml 파일을 생성하고 위 내용을 입력한다. DB 콘솔 접근법은 하단에서 설명하겠다.

4. 패키지 생성, 소스 작성

위와 같이 패키지 혹은 폴더를 생성하고 다음 파일들을 작성한다. 핵심 내용만 보면

  • Member.java
    public class Member {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name="ID")
      private long id;
    
      private String name;
    
      @Column(nullable=true)
      private Integer age;
    }
    
    간단하게 id와 이름, 나이 컬럼을 사용하려 한다. 실행시 테이블 자동생성을 위해 id에 GeneratedValue 어노테이션과 strategy를 설정해 준다.

 

  • MemberController.java
public class MemberController {
	@Autowired
    private MemberService memberService;

    // 멤버 등록 (spring-data-jpa)
    @PostMapping(value = "/member")
    public String createMember(@RequestBody Member member){
        return memberService.createMember(member);
    }

    // 멤버 등록 (jpa EntityManager)
    @PostMapping(value = "/memberjpa")
    public String createMemberJpa(@RequestBody Member member){
        return memberService.createMemberJpa(member);
    }
	
	// 멤버 조회
    @GetMapping(value = "/member")
    public List<Member> getMember(@RequestParam(required = false, defaultValue = "") String name){
        return memberService.getMemberList(name);
    }
}

두 가지 방식의 멤버 등록을 위한 메써드를 준비하고, 등록이 잘 되었는지 확인할 조회 메서드를 하나 만든다.

 

  • MemberService.java
public class MemberService {

	@Autowired
    private MemberRepository memberRepository;

    // spring-data-jpa
    public String createMember(Member member){
    	memberRepository.save(member); // 사용자 등록 처리
        return "success";
    }

    // jpa
    public String createMemberJpa(Member member){
    	log.info("member name : " + member.getName());
    	memberRepository.createUserAuto(member);
    	return "jpa success";
	}

    public List<Member> getMemberList(String name){
        if(name.isEmpty()) {
            return memberRepository.findAll();
        } else {
        	return memberRepository.findByNameLikeOrderByName(name);
        }
    }
}

컨트롤러와 같은 목적으로 생성한다.

 

  • MemberRepository.java

Repository는 우선 spring-data-jpa를 위해 기본적인 JpaRepository를 상속하는 interface를 만들어준다.

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
	List<Member> findByNameLikeOrderByName(String name);
}

이제 JPA를 만들어본다. JpaRepository는 EntityManager에 직접 접근할 수 없으므로 우회로를 만들어주자.

추후 MemberRepository에 상속하기 위한 interface repository를 만드는데 service에서 jpa용으로 만들어 둔 함수를 사용한다.

public interface MemberCustomReposity {
	String createUserAuto(Member member);
}

그리고 해당 인터페이스를 구현할 MemberCustomReposityImpl 파일도 만든다.

@Transactional(readOnly = true)
public class MemberCustomReposityImpl implements MemberCustomReposity {
	@PersistenceContext	// 영속성 컨택스트를 spring-data-jpa 에서 사용하기 위한 어노테이션
	EntityManager em;

	@Override
	@Transactional
	public String createUserAuto(Member member){
		log.info(member.getName());
		em.persist(member);

		Member member2 = em.find(Member.class, member.getId());
		log.info(member2.getName() + " / " + member2.getId());

		return "실행 완료";
	}
}

아주 단순하게 넘어온 멤버객체 이름을 출력하고, 등록된 값의 id로 데이터를 찾아 정보를 출력해 보았다.

@PersistenceContext를 통해 EntityManager를 가져다 사용하고, 클래스 단의 트랜잭션은 readonly 상태로, 트랜잭선 처리가 필요한 메서드는 @Transactional 어노테이션을 추가해야 EntityManager가 1차캐쉬를 플러쉬 하면서 처리할 수 있다.

여기까지 작성한 내용을 MemberRepository에 추가해 주자.

@Repository
public interface MemberRepository extends JpaRepository<Member, Long>, MemberCustomReposity {
	List<Member> findByNameLikeOrderByName(String name);
}

말은 길었지만 정작 코드는 참 쉽다. 이제 서버를 구동하고 테스트를 해보자.

 

5. 테스트

컨트롤러에 작성한 접근 방식을 이용하여 하나씩 테스트해보자.
먼저 spring-data-jpa 로 등록.

success 응답이 왔다.
다음 JPA EntityManager로 등록.


콘솔 로그도 정상 확인.


제대로 등록 되었는지 조회해보자.

제대로 등록되어 있다.

DB에 직접 붙어서 확인하고 싶다면,

http://localhost:8080/h2-console 로 접속한다.

이와같은 화면을 볼 수 있는데 이것이 h2 DB console 이다. 다른건 그대로 두고, JDBC URL 만 본인 설정에 맞게 변경한다.

연결 테스트 해보고 접속해보자.

그럼 SQL을 직접 작성해 볼 수 있는 창이 보인다. 마음껏 쿼리를 날려보자.

 

정리


  • spring-data-jpa를 기반으로 EntityManager를 위한 repository 를 따로 구현한다.
  • EntityManager를 사용하기 위해 @PersistenceContext 어노테이션을 이용한다.
  • JPA는 트랜잭션 기반으로 동작하기 때문에 EntityManager 설정 시 트랜잭션 설정을 해준다. 정도가 되겠다.

해당 내용의 소스 파일은 Github 에 올려두었다.

하나하나 헤딩하는 과정이 힘들지만 모레알처럼 작은 지식들 하나하나가 쌓여가고 있다고 행복회로를 돌려본다.

반응형
반응형

■ JPA (JAVA PERSISTENCE API) 란?

자바 ORM (oriented-releational Mapping) 기술에 대한 API 표쥰 명세.

쉽게 말해 인터페이스의 집합체.

JPA를 이용하기 위해서는 이를 구현 해 줄 수 있는 프레임워크를 사용해야 한다. 

Hibernate, EclipseLink, DataNucleus 가 주로 쓰이는 프레임워크이고

가장 대중적인 프레임워크는 Hibernate (하이버네이트).

 

JPA를 왜 사용해야 하는가?

1. 생산성 향상

  반복적인 코드와 기본 CRUD 용 쿼리를 작성하는 수고를 덜어준다. 

2. 유지보수 편의성

  필드 추가, 삭제 등의 수정사항을 개발자가 직접 유지보수를 위해 코딩해야 했던 노력이 줄어든다. (JPA가 대신 처리함)

3. 페러다임의 불일치 해결

  JAVA 의 객체기반 - DB의 관계형 데이터 기반 

 코드가 디테일 해 질수록 두 페러다임의 불일치가  발생하는데 JPA가 중간에서 해결 가능.

4. 성능 향상

  JPA는 어플리케이션과 DB 사이에서 동작하므로 이렇게 계층이 하나 더 있으면 최적화를 시도해 볼 수 있는 것이 많다. 

5. 데이터 접근 추상화와 벤더 독립성

  JPA는 어플리케이션과 DB 사이에 추상화된 데이터 접근 계층을 제공하여 어플리케이션이 특정 DB 기술에 종속되지 않도록 한다. (페이징 처리의 경우 각 DB마다 사용법이 다르지만, JPA를 사용하면 어떤 DB를 사용하는지 알려주기만 하면 된다)

6. 표준 기술

 JAVA의 ORM 표준 기술이기 떄문에 다른 구현기술로 손쉽게 변경 가능하다.

 

* 개발자는 엔티티 객체를 중심으로 개발하고 데이터베이스에 대한 처리는 JPA 에게 맡겨야 한다.

반응형
반응형


* deploy 경로
WebApp/deploy/프로젝트명.war

* DB 설정 추가하기 ( standalone.xml  파일 )
  => standalone.xml  파일에서 DB설정 추가
- 경로 : /JBOSS/domains/프로젝트명/configuration/standalone.xml
- <datasources> 에 하단 내용 추가

(오라클 기준)

<datasource jndi-name="java:/jdbc/tb" pool-name="tb" enabled="true" use-java-context="true">
   <connection-url>jdbc:oracle:thin:@192.168.200.119:1521:XE</connection-url>
   <driver>oracle</driver>
   <pool>
     <min-pool-size>10</min-pool-size>
     <max-pool-size>15</max-pool-size>
     <prefill>true</prefill>
   </pool>
   <security>
     <user-name>tb</user-name>
     <password>1234</password>
   </security>
   <validation>
     <check-valid-connection-sql>select 1 from dual</check-valid-connection-sql>
     <validate-on-match>false</validate-on-match>
     <background-validation>true</background-validation>
     <background-validation-millis>10000</background-validation-millis>
     <use-fast-fail>false</use-fast-fail>
   </validation>
     <statement>
     <prepared-statement-cache-size>100</prepared-statement-cache-size>
     <share-prepared-statements>true</share-prepared-statements>
   </statement>
</datasource>

 

 -- <Drivers> 에 추가

(오라클 기준)

<driver name="oracle" module="com.oracle">
   <driver-class>oracle.jdbc.OracleDriver</driver-class>
</driver>



* 오라클 드라이버 추가

/JBOSS/jboss-eap-7.4/modules/system/layers/base/com/ 

위 경로를 보면 다양한 DB연결 api들이 있다. 해당 경로에 oracle 폴더가 있는지 확인하고 없다면 /oracle/main/ 추가.

ojdbc 등 DB접속에 필요한 파일을 복사해 넣고, 넣은 파일들을 기준으로 

동일 폴더에 module.xml 파일 생성 후 하단 내용 추가.

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="com.oracle">
    <resources>
	     <resource-root path="ojdbc6-11.2.0.3.jar" />
	     <resource-root path="ojdbc8-19.8.0.0.jar" />
         <resource-root path="orai18n-19.3.0.0.jar" />
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
    </dependencies>
</module>

resource-root 부분을 파일에 맞게 수정


* Web.xml
경로 : 프로젝트 소스 내 /WEB-INF/web.xml 파일 수정

<resource-ref>
    <description>Datasource Contents Server</description>
    <res-ref-name>jdbc/tb</res-ref-name> <!-- JNDI 명 -->
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>



* jboss-web.xml 
경로 : 프로젝트 소스 내 /WEB-INF/jboss-web.xml
jboss-web.xml  수정. (없으면 생성)

<!DOCTYPE jboss-web PUBLIC
   "-//JBoss//DTD Web Application 5.0//EN"
   "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd">
   
<jboss-web> 
    <resource-ref>
        <res-ref-name>jdbc/jdbc/tb</res-ref-name>
        <jndi-name>java:/jdbc/tb</jndi-name> <!-- standalone.xml 파일의 JNDI-NAME -->
    </resource-ref>
</jboss-web>

 

반응형
반응형

리엑트는 사용자 정의 태그를 만드는 언어다.

사용자 정의 태그를 리엑트에서는 컴포넌트 라고 부른다.

 

리엑트를 사용하는 것은 결국 컴포넌트를 만들고 사용할 줄 알아야 하는 것이다.

 

그럼 컴포넌트를 만들고 활용해 보자.

 

 

App.js 의 내용을 위와같이 수정해보았다.

 

4~8라인의 funciotn Header 부분이 사용자 정의 테그를 정의한 것이다.

사용자 정의 테그를 만들때에는 함수 명칭의 첫 글자를 무조건 대문자로 해야 한다.

 

13라인의 <Header></Header> 부분이 위에서 정의한 Header 컴포넌트를 적용한 부분이다.

컴포넌트 명칭을 HTML 태그처럼 적어주기만 하면 바로 화면에 적용되는걸 볼 수 있다.

 

 

그러면 사용자 정의 태그를 좀 더 만들어보자.

위에서는 Header 부분을 만들었으므로 아래에서는 nav와 article 부분을 컴포넌트로 만들어보자.

 

 

만들고 나면 위의 이미지와 같다.

 

주의사항대로 컴포넌트를 정의할 때 명칭을 대문자로 하였고,(10, 20라인)

해당 명칭을 HTML 태그처럼 기입해주었다. (31, 32라인)

 

이렇게 적용하면 컴포넌트를 만들기 전이나

컴포넌트를 만든 후나 결과 화면은 똑같다.

 

하지만 컴포넌트로 구성했기 때문에

- 해당 컴포넌트만 따로 공유하기 쉽고

- 컴포넌트의 정의 부분만 수정하면 컴포넌트를 적용한 부분은 일괄 수정이 된다는 장점이 있다.

 

이것이 리엑트의 본질! 컴포넌트다.

반응형
반응형

저번 시간에 create-react-app 설치를 끝내고

마지막에 설치 완료가 되었는지 확인하는 차원에서 실행했던 그 화면이 어떻게 나왔나 가볍게 살펴보자.

 

1. 수정

create-react-app 의 구조를 살펴보면 (좌측 폴더 트리)

프로젝트 명 아래에 여러 폴더가 있고, 그 중 src 폴더를 보면..

index.js 파일이 있다. 

이 파일이 프로그램이 시작되면 자동으로 실행되는 파일로써

프로그램의 입구 라고 보면 된다.

 

index.js 파일의 소스를 보면,

9라인에 <App /> 라는 테그가 보이는데

이 테그는 4라인데 있는 import App from './App'; 라고 보면 된다.

이걸 하나씩 풀어보면 다음과 같다.

import : 다른 경로의 파일이나 내부 함수등을 참조할 때 사용하는 명령어

App : import 된 파일이 이 페이지에서 사용될 명칭을 말한다. 여기서는 App 이라는 명칭으로 사용하겠다고 선언 한 것이고, 이 명칭이 9라인의 <App /> 인 것.

from './App' : 참조할 파일이 있는 경로. ( 참고로 ./ 는 현재 폴더를 말하고 ../는 현재 폴더의 상위 폴더를 말한다. )

'./App' 는 확장자 .js 가 생략되어 있는 것으로, 해당 파일은 동일한 폴더 내의 App.js 파일을 가르킨다.

 

React 자체가 새로운 테그를 정의해서 사용하는 언어이므로

App 이라는 테그를 정의해서 사용한 것을 볼 수 있다.

 

3라인의 ./index.css : 현재 파일의 디자인 관련 내용이 들어있는 파일.

11라인의 document.getElementById('root') : root 라는 아이디에 매핑하라는 뜻으로서,

public 폴더 밑에 index.html 파일에 id="root" 가 존재하며, 이 테그에 매핑한다는 뜻이다.

 

그럼 App.js 파일은 뭔지 들여다보자.

 

App.js 파일의 6라인~21라인이 index.js에 참조 된다는걸 알 수 있다.

만약 해당 라인을 내 마음대로 수정한다면, 나중에 화면을 띄웠을 때 수정된 화면을 볼 수 있을 것이다.

 

4번라인 function App() 을 보다시피 함수로 호출되는 형태인가보다.. 하고 추측해 본다.

2라인의 ./App.css : 해당 페이지의 디자인 부분 코딩이 포함된 파일

 

 

2. 배포

배포는 command line 에서 명령어 타이핑으로 할 것이다.

우선 기존에 npm start 를 해서 서버가 계속 동작중인 상태라면 서버를 종료해주자.

 

터미널이 

이 상태일텐데

 

ctrl+c 를 누르면 서버를 종료할 수 있다.

나는 컨트롤+c를 눌렀더니 일괄 작업을 끝내시겠습니까? 라는 메세지가 떠서 y를 눌렀다.

 

서버가 종료되었다면 npm run build 를 입력하여 소스를 빌드한다.

현재 빌드중...

 

 

빌드가 완료된 모습..

 

 

빌드가 완료되면 좌측 프로젝트 트리에 build 폴더가 생성된다.

그 안의 index.html 파일을 확인해 보면 html 테그가 일렬로 쭉 나열되어 있는걸 확인할 수 있다.

 

이건 배포용 파일은 가독성에 신경쓸 필요가 없으므로 용량을 최대한 줄이는데 초점을 맞춰 개행과 같은 공백을 없앤 것이다.

 

그럼 이제 빌드 된 파일을 실행해 보...기 전에 한가지 알아야 할 게 있다.

아까 빌드할때 나온 메세지를 살펴보면 

 

The build folder is ready to be deployed.
You may serve it with a static server:

  npm install -g serve
  serve -s build

 

라는 메세지가 있다. 위에 빌드가 완료된 이미지에서도 확인 가능하다.

 

serve -s build 라는 명령어를 통해 실행할 수도 있다고 알려주고 있다.

 

여기서 serve 는 웹서버이고, -s 는 사용자가 어떤 경로로 들어오건 index.html 을 실행해주겠다는 것이고,

build는 build 폴더에서 실행하겠다 라는 뜻.

 

serve는 node.js 로 만들어진 어플리케이션이므로 간편하게 실행할때는 npx 명령어를 사용할 수 있다.

 

npx serve -s build 명령어를 입력해보자.

명령어를 입력했더니 serve가 설치되지 않았으니 설치할거냐는 메세지가 보이고, y를 눌러 설치해주었다.

 

그 후 하단에 접속 가능한 경로가 나타났다.

위 경로로 들어가보면 배포된 페이지가 잘 뜨게 된다!

반응형

+ Recent posts