MYSQL GROUP BY(incompatible with sql_mode=only full group by)에 대해 알아보자

 

로컬 및 개발 환경에서 멀쩡히 돌아가던 쿼리가

테스트 서버에 올리고 나니 다음과 같은 Exception을 뱉었다.

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column '칼럼명' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

: group by 절에 포함되지 않은 칼럼(집계되지 않은 칼럼(nonaggregated column))을 select 절에서 뽑아올 경우, 어느 칼럼을 표시해야 할 지 몰라 exception 이 발생하는 현상이다. 

only_full_group_by 란?

mysql 5.7버전부터 sql_mode로 추가된 설정으로

집계되지 않은 칼럼을 select 절에서 뽑아오는 경우 exception을 낼지 exception 을 내지 않고 동작할지 결정하는 옵션이다.

 

간단히 예를 들면.

아래와 같이 사람 별로 영어 점수 총 합계를 조회하는 쿼리가 있다.

1
2
3
4
5
SELECT id as '아이디'
       ,  phone_num as '전화번호'
       ,  SUM(eng_point) as '영어점수합계'
FROM  sampletable
GROUP BY id
cs

phone_num (전화번호) 은 유니크한 값으로 다른 레코드와 중복될 수 없는 값이란 걸 개발자는 알겠지만,

DB입장에선 phone_num의 경우 어떤 값을 표시해야 할지 알 수 없다. (이를 집계되지 않은 칼럼 nonaggregated column)이라 칭함)

 

* 동일한 쿼리를 ORACLE 에서 실행시 적합하지 않은 group by 사용으로 exception 이 발생한다.

* mysql 에서 실행시 only_full_group_by 옵션의 활성화/비활성화 상태에 따라 exception이 발생/발생하지 않는다.

 

[해결 방법]

1. 쿼리를 수정한다.

1) 쿼리 자체를 수정. 

1-1) group by 절에 nonaggregated column 추가

1
2
3
4
5
6
SELECT  id as '아이디'
       ,  phone_num as '전화번호'
       ,  SUM(eng_point) as '영어점수합계'
FROM  sampletable
GROUP BY id, phone_num
 
cs

 

1-2) 서브쿼리 사용(경우에 따라 left outer join 사용)

1
2
3
4
5
6
7
  SELECT  id as '아이디'
       ,  phone_num as '전화번호'
       ,  ( SELECT SUM(eng_point) 
            FROM  sampletable
            WHERE st.id = id 
            GROUP BY id) as '영어점수합계'            
FROM  sampletable st
cs

 

2) select 절의 집계되지 않은 칼럼에 ANY_VALUE(칼럼) 함수 추가

1
2
3
4
5
SELECT  id as '아이디'
       ,  ANY_VALUE(phone_num) as '전화번호'
       ,  SUM(eng_point) as '영어점수합계'
FROM  sampletable
GROUP BY id
cs

 

2. mysql 설정파일을 수정한다. 

2-1) 현재 옵션 상태를 확인한다.

 

sql_mode 조회.

> select @@sql_mode;

 

sql_mode 조회 결과.

| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,                                                                                        DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |

ONLY_FULL_GROUP_BY 이 포함되어 있다면 해당 설정이 활성화 되어 있음을 의미.

 

2-2) my.cnf 설정파일을 열어 해당 부분을 찾아 제거

2-3) mysql 재기동.

 

※ 머릿속에서 나온 예제이므로 적합하지 않을 수 있으며, 실제 동작하지 않는 쿼리일 수도 있다.

 애초에 쿼리를 잘 짜면 이 같은 문제가 나올 수 없다...

 

반응형

jungpyo9@gmail.com

github.com/develo-pyo 

많이 부족한 백엔드 개발자입니다.

 

어설프게 알고 넘어갔던 부분은 추후 추가적으로 학습하고 보완하기 위해,

문제를 경험하고 해결했던 방법이 있다면 공유와 기록을 하기 위해 블로그를 하고 있습니다.

 

얕은 지식으로 작성한 글이 많아 잘못된 정보가 다수 존재 할 수 있으니, 이 부분은 감안하시어 읽어주시면 좋겠습니다.

혹여 게시글을 읽으실 때 잘못된 내용이 보이시거나, 공유해주실 수 있는 지식이 있다면 댓글 혹은 메일 부탁드립니다.

 

감사합니다.

반응형

Jboss 이미지 경로 설정 (정적 컨텐츠 경로 지정)

 

jboss home directory/standalone/configuration/ 경로에 위치한

standalone.xml 를 수정해준다.

standalone.xml 에 

아래의 설정을 추가해 준다.

<location name="/images" handler="Images"/>
<file name="Images" path="/usr/local/images" directory-listing="true"/>
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  <subsystem xmlns="urn:jboss:domain:undertow:2.0">
            <buffer-cache name="default"/>
            <server name="default-server">
                <http-listener name="default" socket-binding="http"/>
                <host name="default-host" alias="localhost">
                    <location name="/" handler="welcome-content"/>
                    <location name="/images" handler="Images"/>
                    <filter-ref name="server-header"/>
                    <filter-ref name="x-powered-by-header"/>
                </host>
            </server>
            <servlet-container name="default">
                <jsp-config development="true" check-interval="2" modification-test-interval="2" recompile-on-fail="true"/>
                <websockets/>
            </servlet-container>
            <handlers>
                <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
                <file name="Images" path="/usr/local/images" directory-listing="true"/>
            </handlers>
            <filters>
                <response-header name="server-header" header-name="Server" header-value="WildFly/9"/>
                <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
            </filters>
</subsystem>
cs

domain/images/sample.jpg와 같이 /images 경로가 포함된 호출이 들어오면

/usr/local/images (WAS가 설치된 경로를 기준 D:에 설치되어있다면 D:~)경로에서 파일을 찾는다.

 

[설정 확인]

설정을 마쳤으면

http://localhost:8082/images/ 를

호출해본다.

위와 같은 화면이 나오면 성공.

 

[설정확인2]

/usr/local/images 경로에

.html, .jpg, .css 등의 정적 파일들을 올려주고

 

jboss 서버 호출시 파일을 가져오는 모습을 볼 수 있다.

ex) http://domain/images/파일명

 

 

참고 : https://developer.jboss.org/thread/258975

반응형

Roy Fielding 의 2000년 논문에 의해 소개. 
웹의 장점을 최대한 활용할 수 있는 네트워크 기반의 아키텍쳐, Representational state transfer (REST)  

3가지요소 (리소스메소드메시지)로 구성

HTTP POST, http://test/user/ 

"id":"developyo", 
"blog":"https://developyo.tistory.com/" 

위 요청은 POST 메소드(create), 생성 대상이 되는 리소스인 http://test/user/ 
생성하고자 하는 사용자 내용은 JSON 을 사용 

[HTTP 메소드]

POST,GET,PUT,DELETE 만 사용 

POST : create, Idempotent X 
GET : select, Idempotent O 
PUT : update, Idempotent O 
DELETE : delete, Idempotent O 

POST, GET, PUT, DELETE 는 CRUD 역할 
Idempotent 한 메소드(GET, PUT, DELETE)는 수행 실패시 한 번 더 호출하면 되지만 Idempotent 하지 않은 메소드(POST(create))는 실패시 트랜잭션 처리에 주의해야한다. 


※ Idempotent 는 여러번 호출되도 같은 실행결과가 나오는 것을 의미한다. 
예를 들어 a++는 호출시마다 값이 증가하므로 Idempotent 하지 않으며, a=1 은 호출시 값이 같으므로 Idempotent 하다. 

[REST의 리소스]

REST는 리소스 지향 아키텍쳐 스타일 답게 모든 것을 리소스(명사)로 표현, 각 세부 리소스에는 id 를 붙임 
즉 사용자라는 리소스 타입을 http://test/user 라고 정의했다면 
developyo 란 id를 갖는 리소스는 http://test/user/developyo 와 같이 표현 

POST/생성 의 예 :

HTTP POST, http://test/user/ 

"id":"developyo", 
"blog":"https://developyo.tistory.com/" 

GET/조회 의 예 :

HTTP GET, http://test/user/developyo 

PUT/수정 의 예 :

HTTP PUT, http://test/user/developyo 

"id":"developyo", 
"blog":"https://developyo2.tistory.com/" 

DELETE/삭제 의 예 :

HTTP DELETE, http://test/user/developyo 

 

[REST의 특징]

1. 유니폼 인터페이스(Uniform interface) : 
특정 언어나 기술에 종속받지 않으며 HTTP와 JSON(혹은 XML)을 사용할 수 있는 모든 플랫폼에서 사용이 가능한 느슨한 결합 형태의 구조 
2. 무상태성(stateless) : 클라이언트 상태를 서버쪽에 유지하지 않는다는 의미 (참고)
3. 캐싱 가능 
4. 자체 표현 구조 : 직관적인 이해가 가능 
5. 클라이언트 서버 구조 

[REST 안티 패턴]

1. GET/POST 터널링 : http://test/user?id=developyo&method=update (GET 터널링의 예)
2. 자체 표현 구조가 아닌 경우 (위 1번과 같은 경우) 
3. response code 사용하지 않음 :
response code 는 항상 200(성공) 으로 응답하며, 실제 성공/실패 리턴코드를 body 에 담아 따로 리턴하는 경우

 

 

참고 : https://bcho.tistory.com/953

참고 : https://blog.npcode.com/2017/04/03/rest%EC%9D%98-representation%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80/

 

반응형

맥주소 MAC ADDRESS 확인하기

 

1. 윈도우키+r

2. cmd 입력

3. ipconfig /all 입력 (아래 참고)

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
32
33
34
35
36
37
38
39
40
C:\Users\user>ipconfig /all
 
Windows IP 구성
 
   호스트 이름 . . . . . . . . : JP
   주 DNS 접미사 . . . . . . . :
   노드 유형 . . . . . . . . . : 혼성
   IP 라우팅 사용. . . . . . . : 아니요
   WINS 프록시 사용. . . . . . : 아니요
 
이더넷 어댑터 이더넷:
 
   미디어 상태 . . . . . . . . : 미디어 연결 끊김
   연결별 DNS 접미사. . . . :
   설명. . . . . . . . . . . . : Qualcomm Atheros AR8161 PCI-E Gigabit Ethernet Controller (NDIS 6.30)
   물리적 주소 . . . . . . . . : 99-99-99-99-99-99
   DHCP 사용 . . . . . . . . . : 아니요
   자동 구성 사용. . . . . . . : 예
 
 
무선 LAN 어댑터 Wi-Fi:
 
   연결별 DNS 접미사. . . . :
   설명. . . . . . . . . . . . : Realtek RTL8723AE Wireless LAN 802.11n PCI-E NIC
   물리적 주소 . . . . . . . . : 99-99-99-99-99-99
   DHCP 사용 . . . . . . . . . : 예
   자동 구성 사용. . . . . . . : 예
   링크-로컬 IPv6 주소 . . . . : 
   IPv4 주소 . . . . . . . . . : ...
   서브넷 마스크 . . . . . . . : ...
   임대 시작 날짜. . . . . . . : 
   임대 만료 날짜. . . . . . . : 
   기본 게이트웨이 . . . . . . : 
   DHCP 서버 . . . . . . . . . : 
   DHCPv6 IAID . . . . . . . . : 
   DHCPv6 클라이언트 DUID. . . : 
   DNS 서버. . . . . . . . . . : 
                                 
   Tcpip를 통한 NetBIOS. . . . : 사용
 
cs

유선으로 인터넷을 사용하는 경우 : 이더넷 영역의 물리적 주소가 맥어드레스

와이파이로 인터넷을 사용하는 경우 : 무선 LAN 어댑터 영역의 물리적 주소가 맥어드레스

 

반응형

[ IP 클래스 ]

IPv4 ( 32 bit 주소체계 )

IPv6 (128 bit 주소체계)

 

네트워크 / 호스트

네트워크 주소는 대표주소

호스트 주소는 하나의 네트워크 밑의 피씨를 구분하기 위한 주소

0???????.????????.????????.???????? A클래스 (0~127.x.x.x)

10??????.????????.????????.???????? B클래스 (128~191.x.x.x)

11??????.????????.????????.???????? C클래스 (192~255.x.x.x)

 

* 브로드캐스트(같은 네트워크 안의 모든 피씨에 데이터 전송)는 IP 대역에서 가장 끝번호 

 

ex)

192.125.62.0 은 C클래스. 192.125.62.0 는 네트워크 주소

192.125.62.1 는 PC 2번의 호스트 주소

192.125.62.2 는 PC 2번의 호스트 주소

192.125.62.3 는 PC 2번의 호스트 주소

192.125.62.255 는 브로드캐스트 IP

 

 

[ 서브넷 마스크 ]

IP 대역을 나누기 위함.

비트연산 AND 연산(하나라도 0이면 0)으로 0을 만들 수 있는 수로 서브넷 마스크 사용

 

ex)

192.125.62.0 C 클래스를 2개의 네트워크 대역으로 나누고 싶은 경우,

서브넷마스크로 192.125.62.128 을 사용 

 

네트워크 주소 192.125.62.0 -> 11000000.01111101.00111110.00000000

서브넷마스크 192.125.62.128 -> 11111111.11111111.11111111.10000000

네트워크 대역1 (192.125.62.0~192.125.62.127): 11000000.01111101.00111110.00000000 ~ 01111111네트워크 대역2 (192.125.62.127~192.125.62.255): 11000000.01111101.00111110.10000000 ~ 11111111

* 네트워크 대역1 의 브로드캐스트 IP 는 192.125.62.127(11000000.01111101.00111110. 01111111)

* 네트워크 대역2 의 브로드캐스트 IP 는 192.125.62.255(11000000.01111101.00111110. 11111111)

 

ex2)

192.125.62.0 C 클래스를 4개의 네트워크 대역으로 나누고 싶은 경우, 

서브넷마스크로 192.125.62.192(11111111.11111111.11111111.11000000) 을 사용 

 

ex3)

192.125.62.0 C 클래스를 8개의 네트워크 대역으로 나누고 싶은 경우, 

서브넷마스크로 192.125.62.192(11111111.11111111.11111111.11100000) 을 사용 

반응형

Mybatis like 조건시 문자열 처리

 

 

특정 문자열을 포함한 값을 가져오는 쿼리는

select * from where title like '%대상문자열%'

과 같이 쿼리를 작성 및 사용하는데,

이를 mybatis 에서 사용시 like 뒷 부분을 어떻게 처리해야 할지 난해하다.
*
like '%#{key}%' 와 같이 작성시
아래와 같은 에러로그를 볼 수 있다.

19:16:37,814 INFO  [stdout] (default task-3) ### SQL: SELECT COUNT(*)      FROM banword     WHERE ban_word like '%?%'

 

19:16:37,814 INFO  [stdout] (default task-3) ### Cause: java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0).

19:16:37,814 INFO  [stdout] (default task-3) ; SQL []; Parameter index out of range (1 > number of parameters, which is 0).; nested exception is java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0)., result_cd=0} | RESULT = fail | REMOTEADDR = 127.0.0.1 | PORT = 58841 | TIME = 350 ms

 

해결 :

 

[MySQL]
title like CONCAT('%',#{key},'%')

[Oracle]
title like '%' ||  #{key} || '%'

[MSSQL]
title like '%' + #{key} + '%' 


 

반응형

'back > Mybatis,Ibatis' 카테고리의 다른 글

[mybatis] xml에서 java method 사용  (0) 2020.08.24
[Mybatis] 동적쿼리 (if test) 문자열처리  (4) 2020.03.27
[Ibatis] dtd 경로 문제  (1) 2019.12.30
[Mybatis] $과 # 차이  (0) 2019.12.05

Exception 클래스의 구현과 AOP 설정을 이용한 예외처리

 

API 서버 개발을 하며 케이스별 에러코드와 에러메시지를 response 값으로 던져주어야 했다.

각각의 API(Controller)에 try catch 로 exception 을 잡아 상황별 에러코드 및 에러메시지를 던지기엔 API 갯수가 너무 많았고, 비효율적(노가다)이라 생각하여

enum과 customized Exception class, AOP 설정을 통해 해결했다.

 

1. ENUM 클래스 작성
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
32
33
34
35
36
37
38
39
40
public static enum APIResult {
        SUCCESS (200"success"),
        FAILURE (999"fail"),  //기타 에러
        E_PARAM_NULL(101"input parameter is null"),  // 필수값 누락 에러
        E_FORMAT(102"input parameter data format error"), // 파싱 에러
        E_NETWORK(104"network error"), //네트워크 에러
        
        private int code;
        private String message;
        
        private APIResult(int _code, String _message) {
            this.code = _code;
            this.message = _message;
        }
        
        public static APIResult search(int code) {
            for (APIResult status : APIResult.values()) {
                if(status.getCode() == code) {
                    return status;
                }
            }
            return null;
        }
      
        public int getCode() {
            return code;
        }
 
        public void setCode(int code) {
            this.code = code;
        }
 
        public String getMessage() {
            return message;
        }
 
        public void setMessage(String message) {
            this.message = message;
        }
}
cs
예외 케이스별로 던져줄 Error Code와 Error Message를 Enum 클래스로 작성.
 

2. customized Exception class 작성

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
32
33
34
35
36
37
38
39
40
package ;
 
public class APIException extends RuntimeException {
 
    private int ERR_CODE = APIResult.FAILURE.getCode();
 
    public APIException()  {
        super();
    }
 
    public APIException(String errDesc) {
        super(errDesc);
    }
 
    public APIException(String errDesc, int code) {
        super(errDesc);
        this.ERR_CODE = code;
    }
 
    public APIException(String errDesc, ConstantsAPI.APIResult e){
        super(errDesc);
        this.ERR_CODE = e.getCode(); 
    }  
 
    public APIException(ConstantsAPI.APIResult e){
        super(e.getMessage());
        this.ERR_CODE = e.getCode(); 
    }
 
    public String getErrorDesc() {
        return this.getMessage();
    }
 
    public int getErrCode() {
        return this.ERR_CODE;
    }  
 
}
 
 
cs

RuntimeException 을 상속받는 Exception 클래스를 작성한다.

생성자 오버로딩을 하여 Exception Message 및 Exception Code를 받을 수 있게 한다.

* Enum 클래스를 통으로 받는 생성자 또한 만들어준다.

ex)

APIException(ConstantsAPI.APIResult e){

//생략

}

 
3. Controller 에서 try catch 처리
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
@RequestMapping(value = "/sample", method={RequestMethod.POST}, produces = "application/json; charset=UTF-8")
public ModelAndView sample(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> inParam) throws Exception {
    
    Map<String, Object> result = new HashMap<String, Object>();
    ModelAndView mav = new ModelAndView("jsonView");
    
    try{
        // inParam 유효성 체크
        CommUtil.paramVaildate(new String[] {"tokenId"}, inParam);
        
        sampleService.sampleMethod(inParam);
        
        result.put("result_cd", APIResult.SUCCESS.getCode());
        result.put("result_msg""");
    } catch (APIException ae) {
        result.put("result_cd", ae.getErrCode());
        result.put("result_msg", ae.getMessage());
    } catch (Exception e){
        result.put("result_cd", APIResult.FAILURE.getCode());
        result.put("result_msg", e.getMessage());
    }
    
    mav.addAllObjects(result);
    
    return mav;
}
 
cs
9라인 CommUtil.paramValidate(..); : API 서버의 Controller 이기 때문에 필수값이 제대로 들어왔는지 우선 확인
11라인 sampleService.samplemethod(..); : 서비스 호출(서비스 내부적으로 파싱 및 기타 에러가 나는 경우 try catch로 예외를 잡아 Controller 쪽으로 예외를 미룸..  (생략)
catch문으로 예외를 잡아 error code 및 error message result에 담아 return..

 

* input parameter 유효성 검사

 

향상된 for문을 적절히 사용.

Map에 키가 담겨있지 않거나, 빈 값인 경우 구현해 놓았던 Custom Exception 클래스를 throw.

이때, 위에 작성해 놓은 enum을 활용.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static boolean paramValidate(String[] params, Map<String, Object> inParam) {
        
        boolean _result = true;
        
        try {
            for (String item : params) {
                if(!inParam.containsKey(item)) {
                    throw new APIException(String.format("%s not exist", item), APIResult.E_PARAM_NULL);
                }
                if(String.valueOf(inParam.get(item)).isEmpty) {
                    throw new APIException(String.format("%s is null", item), APIResult.E_PARAM_NULL);
                }
            }
        } catch(APIException e) {
            throw e;
        } catch(Exception e) {
            throw new APIException(APIResult.FAILURE);
        }
        
        return _result;
    }
}
 
cs

 

여기까지만 해도 괜찮지만 Controller 마다 아래의 코드가 들어가는게 마음에 들지 않았다..

1
2
3
4
5
6
7
8
9
try{
 
catch (APIException ae) {
    result.put("result_cd", ae.getErrCode());
    result.put("result_msg", ae.getMessage());        
catch (Exception e){
    result.put("result_cd", APIResult.FAILURE.getCode());
    result.put("result_msg", e.getMessage());
}
cs

 

 

6줄 남짓의 코드를 지우기 위해 

Exception을 공통(일괄?)처리 할 수 있는 @ControllerAdvice 및 @Exceptionhandler 를 사용하여 Exception 처리를 해보았으나..

Exception이 발생하는 경우, log를 찍기 위해 구현해 놓았던 interceptor 가 제대로 동작하지 않는 문제점이 발생.

 

구글링을 해보니, Exception 발생시 interceptor의 postHandle은 타지 않는단다..

 

아래는 구글링으로 찾은 내용..

 

From the javadoc of HandlerInterceptor.postHandle

Intercept the execution of a handler. Called after HandlerAdapter actually invoked the handler, but before the DispatcherServlet renders the view. Can expose additional model objects to the view via the given ModelAndViewDispatcherServlet processes a handler in an execution chain, consisting of any number of interceptors, with the handler itself at the end. With this method, each interceptor can post-process an execution, getting applied in inverse order of the execution chain.

Arguably the MethodArgumentNotValidException is thrown before the method is actually called, it is called in preparing the actual method call. 

Actually the postHandle is only executed after successful execution/invocation of the method.  In case of an exception only the preHandle and afterCompletion methods are called.

>> postHandle은 정상적인 메소드 실행에서만 작동. 예외시 preHandle, afterCompletion 만 호출됨.

 

(출처 : https://stackoverflow.com/questions/20551888/controlleradvice-methods-seem-to-bypass-interceptors)

 

그래서 AOP를 사용해보았다.

 

 

4. AOP 사용

4-1) pom.xml 에 aop 사용을 위한 설정 추가

 

property에 아래의 내용 추가

1
<org.aspectj-version>1.6.10</org.aspectj-version>
cs

 

dependency 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${org.aspectj-version}</version>
</dependency>
 
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${org.aspectj-version}</version>
</dependency>
 
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjtools</artifactId>
    <version>${org.aspectj-version}</version>
</dependency>
cs

 

4-2) SampleAOP 작성

@Around(pointCut)을 선언

@Around("execution(public org.springframework.web.servlet.ModelAndView com.sample.*Controller.*(..))")

설명 >> public 접근제한자이면서, org.springframework.web.servlet.ModelAndView 리턴타입이면서, 

            com.sample. 밑의 Controller로 끝나는 모든 Class 밑의 모든 메소드(인자값 상관없이)가 실행될 때, 해당 함수(around(..))를 전, 후로 실행...

 Controller(pjp.proceed()) 에서 예외 발생시 AOP에서 error code 및 error message를 넣어 return

 

* advice : @Around, @Before, @After 등..  해당 어노테이션이 붙은 코드의 실행 시점을 의미

* pointCut : 정규식을 사용하여 어떤 joinPoint 를 사용할지 지정

* joinPoint : advice가 적용될 수 있는 메소드(함수)

 

* xml 빈 등록 잘못 할 경우 aop 를 두번 타는 문제가 발생할 수 있으니 주의.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
package ;
 
import java.util.HashMap;
import java.util.Map;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
 
@Component
@Aspect
public class SampleAOP {
    
    @Around("execution(public org.springframework.web.servlet.ModelAndView com.sample.*Controller.*(..))")
    public ModelAndView around(ProceedingJoinPoint pjp) {
        ModelAndView mav = null;
        
        try {
              mav = (ModelAndView) pjp.proceed();
        } catch(APIException e){
            mav = new ModelAndView("jsonView");
            e.printStackTrace();
            Map<String, Object> result = new HashMap<String, Object>();
 
            result.put("result_cd", e.getErrCode());
            result.put("result_msg", e.getMessage());
            
            mav.addAllObjects(result);
        } catch (Throwable e) {
            mav = new ModelAndView("jsonView");
            e.printStackTrace();
            Map<String, Object> result = new HashMap<String, Object>();
 
            result.put("result_cd"0);
            result.put("result_msg", e.getMessage());
            
            mav.addAllObjects(result);
        }
        return mav;
    }
}
 
cs

 

 

5. Controller 에서 try catch 처리 수정

 

예외처리를 AOP에서 일괄적으로 관리하니 기존의 try catch 를 지워준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 @RequestMapping(value = "/sample", method={RequestMethod.POST}, produces = "application/json; charset=UTF-8")
    public ModelAndView sample(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> inParam) throws Exception {
        
        Map<String, Object> result = new HashMap<String, Object>();
        ModelAndView mav = new ModelAndView("jsonView");
        
        // inParam 유효성 체크
        CommUtil.paramVaildate(new String[] {"tokenId"}, inParam);
            
        sampleService.sampleMethod(inParam);
            
        result.put("result_cd", APIResult.SUCCESS.getCode());
        result.put("result_msg""");
        
        mav.addAllObjects(result);
        
        return mav;
    }
 
cs

 

AOP 를 어설프게나마 사용해 보았다는 걸로 만족...

AOP 는 추후 더 공부해서 개념, 설정, 구현 부분을 따로 포스팅 해보겠다.

 

 

 

* 제 경우 기존 프로젝트에 aop 설정이 부분적으로 되어있었습니다.

  위의 설정 및 소스를 그대로 따라할 경우 aop 동작이 제대로 안될 수도 있습니다.

 

반응형



https://colorscripter.com/


반응형

'etc.' 카테고리의 다른 글

[etc.] nativeQuery 복붙할 때 따옴표(") 제거하기  (0) 2023.04.19
Slack reminder 푸시 설정  (0) 2023.04.07
[IDE] IntelliJ 단축키  (0) 2022.07.11

postman 을 사용하면 여러모로 편리하지만,

리눅스환경에서 외부 api를 호출해야 하는 경우, 아래와 같이 curl 로 호출해보면 된다.

 

1
2
3
4
5
6
7
8
9
1.GET 요청
curl -X GET --data-urlencode "key=value&key2=value2" http://ip:port
 
2.POST 요청
curl -X POST http://ip:port -d '{"objKey":"objValue", "arrayKey":["arrayValue1", "arrayValue2"]}'
 
3.DELETE 요청
curl -X DELETE http://ip:port/key
 
cs

 

[ 요청시 헤더 추가 ]

-H "Content-Type: application/json"

 

[ 요청시 connection timeout ]

--connect-timeout : 타임아웃설정을 걸고 싶을 경우

(따로 해당 옵션을 주지 않아도 default로 일정 시간이 적용되는 것 같다. hang이 알아서 풀린다..)

ex) curl --connect-timeout 300 -X GET ~

 

[ 응답시 헤더 확인 ]

-I : response 헤더정보 확인할 때 사용

(요청에 따른 return 값 없이 500, 404, 200 등의 responseCode만 리턴되는 경우 헤더정보를 보고 확인)

ex) curl -I -X DELETE http://ip:port/key

 

[ 요청 및 응답 세부 정보 확인 ]

-v : 요청한 파라미터 및 리턴 responseCode 등 상세한 request/response 정보를 보고 싶을 때 사용

 

반응형

+ Recent posts