'Back-End'에 해당되는 글 287건

  1. 2019.06.08 PSA , 스프링에서 PSA형식으로 되어있는 것들(인강+블로그)
  2. 2019.06.07 Spring resources [js, css, img] 맵핑 (No mapping for GET)
  3. 2019.06.07 Oracle와 mybatis 연동 실습 (글상세보기 구현)
  4. 2019.06.06 PSA (Portable Service Abstraction)
  5. 2019.06.06 Spring AOP (Aspect Oriented Programming) - 블로그참고
  6. 2019.06.05 Spring 에러 (No Spring WebApplicationInitializer types detected on classpath)
  7. 2019.06.05 MyBatis 설정시 #, $ 차이점
  8. 2019.06.05 Oracle와 mybatis 연동 실습 (글쓰기 구현)

PSA , 스프링에서 PSA형식으로 되어있는 것들(인강+블로그)

Back-End/Spring 2019. 6. 8. 12:57

-PSA란??-


잘 만든 인터페이스


이식가능한 서비스 추상화


PSA를 사용하면 확장성이 좋아지고 (테스트하기 편함)


기술자체를 유동적으로 바꿔서 사용할 수 있다.


스프링이 제공하는 거의 모든 API는 PSA이다.


@Transactional 을 예로 들면


어노테이션과 그 실행 aspect 클래스가 따로 존재한다.


그 aspect는 Transaction 기술과는 독립적인 PlatformTransactionManager라는 인터페이스가 사용된 코드


PlatformTransactionManager 인터페이스의 구현체가 바뀌더라도 TransactionAspect의 코드는 바뀌지 않는다.


구현체들 중에 여러가지가 bean으로 등록 되있는 것들이 있다 ex-JPA


aspect는 Platform TransactionManager를 사용하여 코딩했기 때문에


jpaTransactionManager에서 DatasourceManager로 바뀌더라도 Transaction을 처리하는 aspect 코드는 바뀌지 않는다.


(인터페이스가 가진 기본 기능, 기본 틀은 바뀌지 않는다는 뜻)





-스프링 트랜잭션-


스프링은 코드 기반의 트랜잭션 처리 뿐 아니라 선언적 트랜잭션 (Declarative Transaction)을 지원하고 있다.


스프링이 제공하는 트랜잭션 템플릿 클래스를 이용하거나 설정 파일, 어노테이션을 이용해서 트랜잭션의


범위 및 규칙을 정의할 수 있다.



선언적 트랜잭션 처리


ㄱ. 선언적 트랜잭션은 설정파일이나 어노테이션을 이용해서 트랜잭션의 범위, 롤백 규칙 등을 정의


ㄴ. 다음과 같은 2가지 방식으로 정의함


1. <tx:advice> 태그를 이용한 트랜잭션 처리


2. @Transactional 어노테이션을 이용한 트랜잭션 설정



-예  시-

/**

     * 사업부 정보 등록,수정

     * @param map

     * @return

     * @throws Exception

     */

    @Transactional

    public int saveDivisionData(Map<String, Object> map) throws Exception {

      int cnt = companyDAO.saveDivisionData(map);

       

      // 사업부 수정이 정상적으로 처리되면 대상 사업부를 확산대상으로 지정한다.

      if(cnt == 1){

        companyDAO.saveExtensionTarget(map);

      }

      

        return cnt;

    }



@Transactional을 써주는 이유??


companyDAO.saveDivisionData 에서 처리한 쿼리문이 정상적으로 완료가 되고, companyDAO.saveextensionTarget 에서 처리 도중


에러가 났을 때 companyDAO.saveDivisionData 에서 처리한 쿼리를 자동 rollback(작업 취소) 해주기 위해 사용된다.


만약 저 어노테이션을 써주지 않는다면, 위에꺼는 정상적으로 완료가 되었기 때문에 직접 save 한 division 데이터를


복구 시켜놔야한다.


-인터페이스를 구현한 클래스로 선언된 빈은 인터페이스 메소드에 한해서 트랜잭션이 적용됨


-인터페이스에 붙은 @Transactional 선언은 인터페이스 내의 모든 메소드에 적용됨


-동시에 메소드 레벨에도 @Transactional을 지정할 수 있다. 


-클래스의 @Transaction > 인터페이스의 @Transactional


-@Transactional 적용 대상은 미리 결정하고 애플리케이션 안에서 통일하는게 좋음. 인터페이스와 클래스 양쪽


  에 불규칙하게 @Transaction이 혼용되는건 바람직하지 못하다.



context-transaction.xml 파일에 아래와 같이 선언


<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

      <property name="dataSource" ref="dataSource"/>

    </bean>

    

    <tx:advice id="txAdvice" transaction-manager="txManager">

        <tx:attributes>

            <tx:method name="select*" read-only="true" />

            <tx:method name="list*" read-only="true" />

            <tx:method name="retrieve*" read-only="true" />

            <tx:method name="get*" read-only="true" />

            <tx:method name="find*" read-only="true" />

            <tx:method name="view*" read-only="true" />

            <tx:method name="*Move" read-only="true" />

            <tx:method name="insert*" propagation="REQUIRED" />

            <tx:method name="update*" propagation="REQUIRED" />

            <tx:method name="delete*" propagation="REQUIRED"/>

            <tx:method name="save*" propagation="REQUIRED"/>

            <tx:method name="create*" propagation="REQUIRED"/>

            <tx:method name="merge*" propagation="REQUIRED"/>

            <tx:method name="execute*" propagation="REQUIRED"/>

            <tx:method name="excel*" propagation="REQUIRED"/>

            <tx:method name="CALL" propagation="REQUIRED"/>

            <tx:method name="*" rollback-for="Exception"/>

        </tx:attributes>

    </tx:advice>

    

    <aop:config>

        <aop:pointcut id="requiredTx" expression="execution(* com.????.??..impl.*Impl.*(..))"/>

        <aop:advisor advice-ref="txAdvice" pointcut-ref="requiredTx" />

    </aop:config>


<tx:annotation-driven transaction-manager="txManager" />


// @Transactional을 사용할 때 필요한 설정은 다음 한 줄 뿐이다.


<tx:annotation-driven> 태그는 등록된 빈 중에서 @Transactional이 붙은 클래스나 인터페이스 또는 메소드를 찾아


트랜잭션 어드바이스를 적용해준다.


출처: https://crosstheline.tistory.com/96 [이거 알아영???ㅎㅎㅎ]




-스프링 캐시-


  캐시란?


  복잡한 계산, DB작업, 원격 처리 결과 등을 임시 저장소인 캐시에 저장해뒀다가 동일한 요청이 들어오면


  캐시에 보관해뒀던 기존의 결과를 그대로 돌려줌



  주의할점


  1. 반복적이고 동일한 작업에만 사용 가능


  2. 캐시의 유효성 검사


  ㄱ. 공지사항이 수정되서 DB가 바뀌었는데도 계속해서 캐시의 이전내용을 보여주면 곤란


  ㄴ. 캐시의 내용이 유효하지 않은 시점이 되면 캐시에서 해당 내용을 제거해주는 작업이 필요하다.


  ㄷ. 캐시의 제거는, 주기적으로 하거나 어떤 메소드가 실행될때 캐시를 제거하도록 어노테이션을 이용해서 설정할 수 있다.




출처

https://m.blog.naver.com/PostView.nhn?blogId=kbh3983&logNo=220952478803&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F




@EnableCaching 어노테이션을 사용하면 캐시 관련된 기능이 활성화 된다.


이게 활성화되면 @Cacheable나 @CacheEvict 같은 어노테이션들을 사용할 수 있다.


그렇게 사용할려면 CacheManager가 있어야한다.  


왜냐하면 @Cacheable나 @CacheEvict을 처리하는 aspect가 어딘가에 있기 때문... 그리고 그 aspect에서는 트랜잭션 매니저를 사용한다. 


이 빈에서는 JCacheManager를 사용하고 있고, 의존성에 ehCache(원래는 JCache 였는데 이름이 바뀐거같음) 가 있다.


이 빈을 ehCacheManagerCustomizer로 바꿔주면 ehCacheManager를 쓰게 될것이고,  


그럼에도 @Cacheable, @CacheEvict를 처리하는 cacheaspect코드는 바뀔일이 없다.




추상화 웹 MVC (@Controller 과 @RequestMapping)


지금은 @Controller과 @GerMapping를 사용해서 웹 mvc를 구현

메소드에서 뷰를 리턴하면 해당이 되는 뷰를 출력함


하지만 매핑하는 값이 서블릿일수도 있고 리액티브를 쓰는것일수도 있음.

의존성을 확인하기 전까진 알지 못한다.

그렇기 때문에 매핑하는 값도 추상화로 볼 수 있다.

(즉, 기술 (Servlet, Reactive)를 바꾸어도 코드의 변경이 일어나지 않는다

그리고 테스트 하기도 더 쉽다.)







:

Spring resources [js, css, img] 맵핑 (No mapping for GET)

Back-End/Problems 2019. 6. 7. 14:27

17:29:01.023 [http-nio-8090-exec-8] WARN [org.springframework.web.servlet.PageNotFound.noHandlerFound:1248]-

No mapping for GET /spring02/include/style.css



리소스 맵핑 방법


1. webapp/resources 폴더에 js, img, css 파일들을 저장한다.


2. servlet-context.xml에 <resources mapping="resources/**"location="resources/" /> 경로를 추가시킨다.


3. jsp 페이지에 ${pageContext.request.contextPath}/resources/css/style.css 경로 지정을 한다.


하지만 이렇게 하면 기존에 path경로를 사용하던 페이지의 경로를 전부다 바꿔주어야해서 경로의 공통부분만 path를 사용하고 나머지 부분은

<link ~href> 링크를 사용해 따로 지정해준다.






문제 (맵핑이 안됨) 해결


servlet-context.xml 내용중 일부


1
<resources mapping="/resources/**" location="/resources/" /> //리소스 맵핑을 하는 경로가 resources파일 내로 되어있는데
//css파일이 include파일 하위에 있어서 맵핑이 되지 않았었다.
//css파일을 resources파일 내부로 옮겨주었다.
cs

 

header.jsp 내용중 일부


1
<c:set var = "path" value = "${pageContext.request.contextPath}" /> <!-- 경로설정을 편하게하기 위함 (contextPath태그를 자주쓰니까 간단하고, 호출하기 편하게 path로 만들어놓음) -->
cs


1
<link rel = "stylesheet" href="${path}/resources/style.css" />  //css파일의 경로를 resources폴더 하위로 설정해준다.
cs


:

Oracle와 mybatis 연동 실습 (글상세보기 구현)

Back-End/Spring 2019. 6. 7. 14:13

URL 두가지 비교


http://localhost/spring02/memo/view.do?idx=6

이 방식은 view.do에 파라미터값으로 6을 넘겨주는 것이다. 따라서 idx 값에 따라 유동적으로 url이 변한다.



http://localhost/spring02/memo/view/6

view의 주소가 6으로 고정적으로 되어있다.


view 뒤에 6(6에 해당하는 페이지)을 받으려면 @PathVariable를 사용해서 값을 받아주어야 한다


ex)    http://localhost/spring02/memo/view/6 에서 6을 받고 싶을때..


@RequestMapping("view/{idx}")

public ModelAndView view(@PathVariable int idx, ModelAndView mav)

{

~~~~~~

~~~~

}





-글상세보기 구현-


memo_list.jsp 중 일부


글 내용(메모)를 누르게 되면 글 상세보기 페이지로 이동하게 하는 자바스크립트 구문

1
2
3
4
5
6
<script>
//글내용를 누르게 되면
//글번호를 매개값으로 받고, 그 글번호에 대한 상세정보 페이지를 출력함
function memo_view(idx){
    location.href="${path}/memo/view/"+idx;
}
</script>
cs



1
2
3
4
5
6
7
8
<tr>
        <td>${row.idx }</td>  <!-- row에서 값들을 하나씩 불러온다. -->
        <td>${row.writer }</td>
        <!--메모 내용을 클릭하면 메모뷰를 호출하고 그쪽에 글번호를 넘긴다.-->
        <td><a href='#' onclick="memo_view('${row.idx}')">${row.memo }</td>
        <td>
        <fmt:formatDate value="${row.post_date}" pattern="yyyy-MM-dd HH:mm:ss" /></td>
</tr>
cs



MemoController.java 중 일부

1
2
3
4
5
6
7
8
//http://localhost/spring02/memo/view/6 => @PathVariable
    @RequestMapping("view/{idx}")
//@PathVariable 어노테이션을 사용해서 글 번호값을 받아오고
//mav에 setViewName 메소드를 사용해 출력할 페이지를 지정하고 mav에 저장
//아까 받은 글번호를 매개값으로 출력페이지로 이동하게하고 그 이름을 "dto"라 하고 mav에 저장하고
//mav를 리턴한다.

    public ModelAndView view(@PathVariable int idx, ModelAndView mav) 
    {
        mav.setViewName("memo/view");     //출력할 페이지를 지정
        mav.addObject("dto",memoService.memo_view(idx));    //데이터를 저장
        return mav;            //출력 페이지로 이동함
    }
cs



MemoServiceImpl.java (인터페이스 구현 클래스)

1
2
3
4
@Override
    public MemoDTO memo_view(int idx) {
        return memoDao.memo_view(idx);
    }
cs


:

PSA (Portable Service Abstraction)

Back-End/Spring 2019. 6. 6. 12:08

-PSA (Portable Service Abstraction)-


비슷한 기술들을 모두 아우를 수 있는 추상화 계층이다.


사용하는 기술(프로그램)이 바뀌더라도 비즈니스 로직의 변화가 없도록 해준다.


예를 들어 트랜잭션 처리를 하고 싶을 때, 사용하는 기술에 따라 JDBC API를 쓸 수도 있고,


iBatis가 제공하는 API 또는 Hibernate가 제공하는 API를 사용할 수도 있다.


비슷한 기능이지만 각 기술마다 구현의 차이가 있기 때문에 특정 기술을 대체 기술로 바꾸려면


적용한 코드를 모두 찾아 바꿔줘야 하기 때문에 스프링에서는 추상화 계층을 통해서 기술이 바뀌더라도


의존성 주입 기능으로 사용할 객체만 바꿔주면 되도록 지원한다.


(정리 : 인터페이스에서 추상메소드를 만들듯이 PSA에서는 추상화된 계층을 통해서 기술이 바뀌어도 의존성 주입 기능을 써서

사용할 객체만 바꿔주면 되도록 지원한다.) 




-트랜잭션 이란?-


더 이상 나눌 수 없는 단위 작업을 의미


EX) 모든 사용자에 대한 레벨 변경 작업은 전체가 성공하든지 전체가 실패해야 한다.

레벨 변경 작업은 부분적으로 성공하거나 여러 번에 걸쳐서 진행할 수 있는 작업이

아니어야 한다. (일관성)

모든 사용자에 대한 레벨 변경 작업 자체가 쪼갤 수 없는 하나의 작업 단위이므로

중간에 예외가 발생하면 아에 작업이 시작되기 이전 상태로 돌려놓아야 한다. (롤백)


DB는 그 자체로 완벽한 트랜잭션을 지원한다.

하나의 SQL 명령을 처리하는 경우는 DB가 트랜잭션을 보장해준다고 믿을 수 있다. 

(왜냐하면 자료를 삭제하거나 삽입할때는 전체를 다 집어넣거나 지우지 않을수 없기때문.. DB에서는 튜플단위로 삭제나 삽입을 하기 때문에..)


하지만 여러개의 SQL이 사용되는 작업을 하나의 트랜잭션으로 묶어야 하는 경우도 있다.

예를 들면 은행에서 계좌 이체 작업은 반드시 하나의 트랜잭션으로 묶여야 한다.

이체를 할 때는 출금 계좌의 잔고는 줄어들고, 입금 계좌에는 증가되어야 한다.

이 때 이체 프로그램은 DB에 최소 두번의 요청을 보낸다.

문제는 첫번째 SQL은 성공했지만 두번째가 장애로 인해 중단되는 경우이다.


이 때 두가지 작업이 하나의 트랜잭션이 되려면 두번째 SQL이 문제가 발생하여 실패한 경우 첫번째가 성공했어도 다시 취소해야 한다.

이런 취소 작업을 "트랜잭션 롤백" 이라고 한다.


반대로 여러개의 SQL을 하나의 트랜잭션으로 처리하는 경우 모든 SQL 수행 작업이 다 성공했다고 DB에 알려줘서 작업을 확정시키는 것을

"트랜잭션 커밋" 이라고 한다.


모든 트랜잭션은 시작하는 지점과 끝나는 지점이 있다,

시작하는 방법은 한가지이지만, 끝나는 방법은 두가지가 있는데

하나는 모든 작업을 무효화하는 롤백이고, 모든 작업을 확정하는 커밋이다.

그리고 애플리케이션 내에서 트랜잭션이 시작되고 끝나는 위치를 "트랜잭션 경계" 라고 한다.


이 트랜잭션 경계는 하나의 Connection이 만들어지고 닫히는 범위 안에 존재한다.



[트랜잭션 동기화]


트랜잭션 동기화란 특정 Service 에서 트랜잭션을 시작하기 위해 만든 Conection 객체를 특별한 저장소에 보관해두고 이후에 

호출되는 곳에서 지정된 Connection을 가져다 사용하게 하는 것이다.


문제는 멀티스레드 환경에서도 안전한 트랜잭션 동기화를 구현하는 일이 기술적으로 어려운데, 스프링은

JdbcTemplate과 더불어 이런 트랜잭션 동기화 기능을 지원하는 간단한 유틸리티 메소드를 제공한다.


스프링이 제공하는 트랜잭션 동기화 관리 클래스는 "TransactionSynchronizationManager" 이다.

이 클래스를 이용해 먼저 트랜잭션 동기화 작업을 초기화 하도록 요청한다.





-MVC-


MVC란 (Model View Controller) 에플리케이션을 세가지의 역할로 구분한 개발 방법론이다.

아래의 그림처럼 사용자가 Controller를 조작하면 Controller는 Model을 통해서 통해서 데이터를 가져오고

그 정보를 바탕으로 시각적인 표현을 담당하는 View를 제어해서 사용자에게 전달하게 된다.







-Web와 MVC-


위의 개념을 웹에 적용



    1. 사용자가 웹사이트에 접속한다. (Uses)
    2. Controller는 사용자가 요청한 웹페이지를 서비스 하기 위해서 모델을 호출한다. (Manipulates)
    3. 모델은 데이터베이스나 파일과 같은 데이터 소스를 제어한 후에 그 결과를 리턴한다.
    4. Controller는 Model이 리턴한 결과를 View에 반영한다. (Updates)
    5. 데이터가 반영된 VIew는 사용자에게 보여진다. (Sees)



-Codelgniter과 MVC-


  Controller


  사용자가 접근 한 URL에 따라서 사용자의 요청사항을 파악한 후에 그 요청에 맞는 데이터를 Model에 의뢰하고, 데이터를 View에 반영해서

  사용자에게 알려준다.



  Model


  일반적으로 CI의 모델은 데이터베이스 테이블에 대응된다.

  이를테면 Topic 이라는 테이블은 topic_model 이라는 Model을 만든다.

  그런데 이 관계가 강제적이지 않기 때문에 규칙을 일관성 있게 정의하는 것이 필요하다.



  View


  View는 클라이언트 측 기술인 html / css / javascript 들을 모아둔 컨테이너이다.

 



Controller과 Model과 View 간의 관계







출처

https://opentutorials.org/course/697/3828

:

Spring AOP (Aspect Oriented Programming) - 블로그참고

Back-End/Spring 2019. 6. 6. 11:57

AOP는 관점 지향 프로그래밍으로 "기능을 핵심 비즈니스 기능공통 기능으로 '구분'하고, 공통 기능을 개발자의 코드 밖에서

필요한 시점에 적용하는 프로그래밍 방법" 이다.




언제 사용되는가??


  

  성능 검사


  트랜잭션 처리


  로깅


  예외 반환


  검증



예를들어 @Transactional, @Cache 같은 어노테이션들은 AOP를 활용하여 동작하게 된다.



구성요소

설명 

 JoinPoint

모듈의 기능이 삽입되어 동작할 수 있는 실행 가능한 특정 위치 

 PointCut

어떤 클래스의 어느 JoinPoint를 사용할 것인지를 결정 

 Advice

각 JoinPoint에 삽입되어져 동작할 수 있는 코드 

 Interceptor

InterceptorChain 방식의 AOP 툴에서 사용하는 용어로 주로  한개의 호출 메소드를 가지는 Advice

 Weaving

PointCut에 의해서 결정된 JoinPoint에 Advice를 삽입하는 과정 (CrossCutting)

 Introduction

정적인 방식의 AOP 기술 

 Aspect

PointCut+Advice+(Introduction) 



패키지를 나눌때 핵심 비즈니스 기능과 공통 기능으로 구분해야 하지만,

평소에 작성하던 프로젝트에는 보통 MVC  구조로 작성되어 있을 것이고, 해당 코드는 핵심 비즈니스 로직이다.

따로 AOP 패키지로 빠져있는 것이 여기서는 공통 로직이다. (분리되어있다)


기존에 작성한 핵심 비즈니스 로직에는 AOP 때문에 단 한글자도 코드가 변경된 것이 있어서는 안된다.

(왜냐하면 AOP는 공통된 기능을 분리를 시키는 거지, 핵심기능을 삭제하거나 변경하는 것이 아니기 때문!!)



-참고-


(나머지 Controller, Service, Dto, Dao는 생략)


LogAspect.java

package com.example.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
@Aspect
@Component
public class LogAspect {
    Logger logger =  LoggerFactory.getLogger(LogAspect.class);
    
    //BookService의 모든 메서드
    @Around("execution(* com.example.demo.service.BookService.*(..))")
    /*@Around("execution(* com.example.demo.controller..*.*(..))")*/
    /*@Around("execution(* com.example.demo..*.*(..))")*/
    public Object logging(ProceedingJoinPoint pjp) throws Throwable {

        logger.info("start - " + pjp.getSignature().getDeclaringTypeName() + " / " + pjp.getSignature().getName());
//메소드 실행전 로그 호출
        Object result = pjp.proceed();

        logger.info("finished - " + pjp.getSignature().getDeclaringTypeName() + " / " + pjp.getSignature().getName());
//메소드 실행후 로그 호출
        return result; //Object를 리턴
    }
}

 

MVC를 위해서 Controller, Service, Dto, Dao 등 구색만 갖췄고, Dao는 DB연결 없이 임의의 Book 객체 3개를 넣어놓았다.


LogAspect.java 를 제외하고는 일반적인 예제, 사용 프로젝트 구성과 유사하다.


LogAspect가 이제 핵심 비즈니스 로직과 분리된 공통 로직인 것을 확인할 수 있고, 예제에서는 메소드를 실행하기 전 / 후로 로그를 찍어서

디버깅하기 좋은 코드를 만들었다.


@Aspect, @Component로 이 클래스가 AOP를 바라보는 관점을 정의하고 bean으로 등록하는 것을 정의했다. 


@Around는 메소드의 실행 전 / 후에 공통로직을 적용하고 싶을 때 사용하고 @Before는 메소드 실행 전에, @After은 메소드 실행 후에 공통로직을

적용하고 싶을 때 사용한다.


즉 어느시점에 적용할 것인지를 정의하는 것이다.


@Around ("execution( * com.example.demo.service.BookService.*(...))") 를 통해서 어떤 메소드들이 이 AOP를 적용받을 것인지를 정의했다.


execution(* com.example.demo.service.BookService.*(..))는 com.exemple.demo.service 패키지의 BookService의 모든 

메소드가 적용받을 것이라고 한 것이다.


다양한 표현식을 이용해서 원하는 부분에만 적용할 수 있을 것이다.




======================================================================================================


공통으로 사용될 메소드를 살펴보면 Object를 리턴하도록 되어있는데 아주 중요하다.


왜냐하면 AOP는 메소드를 가로채서 (Proxy) 앞이나 뒤에 필요한 공통로직을 한다고 했기 때문이다.


즉 proceed( )에서 정상적으로 메소드를 실행한 후 리턴 값을 주는데 가로채서 어떤 action을 한 후에 기존 리턴 값을 되돌려 주지

않으면 가로챈 AOP가 결과값을 변경한 것, 지워버린 것과 다름없다.


위 메소드에서는 단순하게 앞 / 뒤로 추가 로깅을 찍어주고 기존 메소드가 실행될 수 있게 pjp.proceed( ); 를 호출했다.


아래 결과를 보면 좀 더 이해할 수 있다.




  

  localhost:8080/으로 접속했을 때 @Around("execution(* com.example.demo.service.BookService.*(..))")의 로그다.


  BookService의 모든 메서드에서만 aop가 적용되므로 helloService의 메서드에는 적용이 되지 않는다.



2018-11-23 12:09:16.834  INFO 11348 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2018-11-23 12:09:16.834  INFO 11348 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2018-11-23 12:09:16.839  INFO 11348 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 5 ms
2018-11-23 12:09:16.840  INFO 11348 --- [nio-8080-exec-1com.example.demo.aop.LogAspect           : start - com.example.demo.service.BookService / findBookByTitle
Book [title=spring, isbn=1488526510, price=30000]
2018-11-23 12:09:16.844  INFO 11348 --- [nio-8080-exec-1com.example.demo.aop.LogAspect           : finished - com.example.demo.service.BookService / findBookByTitle
cs



  이번에는 @Around("execution(* com.example.demo.controller..*.*(..))")의 로그다.


  controller 하위 패키지까지의 모든 메서드가 aop에 적용된다.

 

  service는 controller패키지 하위에 있는 패키지가 아니므로 찍히지 않는 것을 볼 수 있다.



1
2
3
4
5
6
7
2018-11-23 12:15:47.981  INFO 11348 --- [nio-8080-exec-3] com.example.demo.aop.LogAspect           : start - com.example.demo.controller.MyController / index
2018-11-23 12:15:47.981  INFO 11348 --- [nio-8080-exec-3] com.example.demo.aop.LogAspect           : start - com.example.demo.service.BookService / findBookByTitle
2018-11-23 12:15:47.984  INFO 11348 --- [nio-8080-exec-3] com.example.demo.aop.LogAspect           : start - com.example.demo.repository.BookDao / findBookByTitle
2018-11-23 12:15:47.988  INFO 11348 --- [nio-8080-exec-3] com.example.demo.aop.LogAspect           : finished - com.example.demo.repository.BookDao / findBookByTitle
Book [title=spring, isbn=1488526510, price=30000]
2018-11-23 12:15:47.988  INFO 11348 --- [nio-8080-exec-3] com.example.demo.aop.LogAspect           : finished - com.example.demo.service.BookService / findBookByTitle
2018-11-23 12:15:47.988  INFO 11348 --- [nio-8080-exec-3] com.example.demo.aop.LogAspect           : finished - com.



출처: https://jeong-pro.tistory.com/171 [기본기를 쌓는 정아마추어 코딩블로그]


:

Spring 에러 (No Spring WebApplicationInitializer types detected on classpath)

Back-End/Problems 2019. 6. 5. 18:19

메모를 작성후 확인을 누르면 저장이 되어야되는데 저장이 되지 않음..



첫번째 오류 - Spring 에러 (No Spring WebApplicationInitializer types detected on classpath)



두번째 오류 -17:29:01.023 [http-nio-8090-exec-8] WARN [org.springframework.web.servlet.PageNotFound.noHandlerFound:1248]-

No mapping for GET /spring02/include/style.css



첫번째 오류 해결중







두번째 문제 (맵핑이 안됨) 해결


servlet-context.xml 내용중 일부


1
<resources mapping="/resources/**" location="/resources/" /> //리소스 맵핑을 하는 경로가 resources파일 내로 되어있는데
//css파일이 include파일 하위에 있어서 맵핑이 되지 않았었다.
//css파일을 resources파일 내부로 옮겨주었다.
cs
 

header.jsp 내용중 일부


1
<c:set var = "path" value = "${pageContext.request.contextPath}" /> <!-- 경로설정을 편하게하기 위함 (contextPath태그를 자주쓰니까 간단하고, 호출하기 편하게 path로 만들어놓음) -->
cs


1
<link rel = "stylesheet" href="${path}/resources/style.css" />  //css파일의 경로를 resources폴더 하위로 설정해준다.
cs


:

MyBatis 설정시 #, $ 차이점

Back-End/MyBatis 2019. 6. 5. 17:54

mybatis를 사용하다 보면 SQL문을 작성할때 전달받은 parameter 변수를 지정해주게 되는데

일반적으로 #{변수명} 과 같은 방식으로 작성을 많이 한다.


하지만 mybatis 에서는 #{변수명} 이외에도 ${변수명} 방법도 사용할 수 있다. 이 둘의 차이점은 다음과 같다.



1. #{변수명}



  <sql id="test" parameterType="hashmap">
      SELECT
          *
      FROM
          TB_member
      WHERE
          id = #{id}
  </sql>


위와같이 #{변수명} 으로 사용하게 되면 mybatis는 preparedStatement 객체에 자동으로 id를 set 해주게 된다.

SELECT * FROM TB_member WHERE id = ? 형식으로 전환이 되고 해당 변수를 set 해준다.



2. ${변수명}



  <sql id="test" parameterType="hashmap">
      SELECT
          *
      FROM
          TB_member
      WHERE
          id = ${id}
  </sql>


해당 방식으로 지정시 해당 변수가 쿼리에 포함이 된다.

즉 SELECT * FROM TB_member WHERE id = 'test' 형식으로 SQL이 실행된다.

컬럼명이나 테이블명이 동적으로 변경되어야 할때 사용하면 좋은데, 해당 방법의 최대 단점은 악의적으로 parameter 조작시 보안에

안전하지 못하다는 것이다.

SQL Injection 공격등에 취약하다.


parameter 변수 등은 가급적이면 #{변수명} 과 같은 방식으로 사용하되, 필요에 따라 ${변수명} 방식으로 사용해도 나쁘지는 않다.



출처: https://fruitdev.tistory.com/70 [과일가게 개발자]







:

Oracle와 mybatis 연동 실습 (글쓰기 구현)

Back-End/Spring 2019. 6. 5. 17:30

글쓰기 구현


이름과 메모를 적고 "확인" 을 누르면 컨트롤러에 매핑한 insert.do 로 이동



memo_list.jsp 중 일부 (글쓰기 폼 작성)


<h2>한줄 메모장</h2>
 
<form method="post" action="${path }/memo/insert.do"<!-- 글쓰기 폼을 작성 insert.do로 넘김 -->
이름 : <input name = "writer" size = "10" >
메모 : <input name = "memo" size = "40" >
<input type = "submit" value="확인">
</form>




MemoController.java 중 일부 (컨트롤러)


이름과 메모값을 둘다 가지고 있는 클래스가 MemoDTO이기 때문에 DTO를 매개변수로 받고,

이름과 메모를 memoService에 insert 한다.

마지막으로 list.do로 리턴하면 목록이 갱신된다.


MemoService.java에 insert 메소드가 2개가 있는데 궂이 2개를 매개변수로 받는 메소드를 사용한 이유는

일반 DTO로 받으면 다시 풀어야 하기 때문에 그렇다.


1
2
3
4
5
6
//폼에서 넘긴값을 읽을때는 
    @RequestMapping("insert.do")
    public String insert(MemoDTO dto) {
        memoService.insert(dto.getWriter(), dto.getMemo());
        return "redirect:/memo/list.do";
    }
cs




MemoDAO.java (흰색 부분이 글쓰기 구현)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.spring02.model.memo.dao;
 
import java.util.List;
 
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
 
import com.example.spring02.model.memo.dto.MemoDTO;
 
public interface MemoDAO {
    //인터페이스는 객체를 생성하지 못함
    //root-context.xml에서 <mybatis-spring:scan base-package = "com.example.spring02.model.memo" />라고 설정을 해놓은것 때문에
    //@Select문 이하~~가 하나의 객체로 완성된다.
    @Select ("select * from memo order by idx desc")
    //현재 list()메소드는 몸체가 없기 때문에 select 어노테이션의 쿼리가 호출됨
    public List<MemoDTO> list();
    
    @Insert("insert into memo (idx,writer,memo) " //메모 테이블의 (idx, writer,memo) 속성에 ? 값을 삽입
    + "values ((select nvl(max(idx)+1,1) from memo)"   // 일단 서브쿼리 (번호를 검색하기 위한 쿼리 select nvl(max(idx)+1,1) from memo)
    + ",#{writer},#{memo})")                            // 코드가 하나도 없으면 null이 나오니까 nvl은 값이 null일때 대체값이다. 즉 값이 null 이면 1이 출력
                                                        //메모 테이블에서 가장큰 idx값에 +1을 한다 
                                                        //이쪽은 마이바티스에서 쓰던 방식 #{변수}를 사용하면 된다.
                                                                        
    public void insert(@Param("writer"String writer,        //Param은 위쪽에서 사용한 마이바티스  #{변수}에 무슨값이 들어갈건지 알려주는 어노테이션이다.
            @Param("memo"String memo);                    //@Param("writer") String writer;  위쪽에서 사용한 writer에 writer 값을 넣는다.
                                                            //@Param("memo") String memo;         위쪽에서 사용한 memo에 memo 값을 넣는다.
    
    
}
 
cs



------47분까지 봄------

: