sitelink1  
sitelink2  
sitelink3  
extra_vars6  
http://blog.empas.com/ahnyounghoe/13223681

간단히 컨트롤러에서 DAO의 조회 요청을 수행하는 시간만을 측정한 것이다.
  long start = System.currentTimeMillis();
  List<ArticleLink> model = articleLinkDao.getDefaultList();
  System.out.println(System.currentTimeMillis() - start);
 
최초 1회만 0.4 초 가까이 소요되고 그 이후에는 모두 0.05 초 이하의 시간이 소요되었다. 데이터가 작아서 그런지 그리 많은 시간이 소요되지는 않았다.
 
코드량으로 보면 iBatis 설정에 약간의 복잡도가 있을 뿐 그다지 무리는 없다. 다만, SELECT를 불필요하게 많이 하는 것으로 볼 수 있다. 만일, 아래 화면에서 별점 숫자에 링크가 걸려서 이들 내역을 조회하는 요구가 있다고 해보자.


앞서의 방법을 썼다면 select 문장에서 컬럼만 몇 개 추가해주면 메모리에 결과가 다 올라와 있어 요청 수행시간이 엄청나게 빠를 것이다. 그러나, 목록에 올라온 모든 링크에 대해서 별점 정보를 죄다 조회하는 요구가 있다고 보기는 힘들기 때문에 이는 적절하지 않은 수행이라 볼 수 있다. 데이터가 많아지면 위의 성능도 상당히 악화될 것으로 추측된다.
 
개선안으로 Count() 함수를 생각해볼 수 있다.
 <resultMap id="article" class="articleLink">
  <result property="id" column="ID" />
  <result property="language" column="LANG" />
  <result property="title" column="TITLE" />
  <result property="link" column="LINK" />
  <result property="regiDate" column="REGI_DATE" />
  <result property="description" column="DESC" />
  <result property="submitter.id" column="USERINFO_REF" />
  <result property="stars" select="selectStarmarker" column="id" />
 </resultMap>

 <!-- SELECT Statements -->
 <select id="selectDefaultList" resultMap="article">
  select ID, LANG, TITLE, LINK, REGI_DATE, DESC, USERINFO_REF
  from T_ARTICLELINK
  order by ID DESC
 </select>
 <select id="selectStarmarker" resultClass="int">
  select COUNT(ARTICLELINK_REF) from T_ART_USER
  where ARTICLELINK_REF = #article_id#
 </select>
 
 
setStarmarker 메소드에서의 로직 수행이 아니라
sqlmap 파일은 조금 간단해졌지만, 쿼리는 더 복잡해졌다. 서브쿼리에 조인이 포함된 구문이다. 유지보수 관점에서는 복잡한 SQL은 엄청난 교육 비용을 요구하기 때문에 아래와 같은 약간의 성능 향상으로는 selectStarmarker 쿼리 수행의 값을 직접 setStars 메소드 인자로 제공하여 설정하는 방식이다.
전체적으로 미세하게 나아진 듯도 하지만 별 차이가 없다. 어차피 COUNT() 함수와 같은 집합/통계 함수를 쓰면 전체 레코드를 검색해야 하니까 큰 차이가 없는 듯 하다.
 
이번에는 JOIN을 해서 COUNT를 하는 방법을 취해보겠다.
 <resultMap id="article" class="articleLink">
  <result property="id" column="ID" />
  <result property="language" column="LANG" />
  <result property="title" column="TITLE" />
  <result property="link" column="LINK" />
  <result property="regiDate" column="REGI_DATE" />
  <result property="description" column="DESC" />
  <result property="submitter.id" column="USERINFO_REF" />
  <result property="stars" column="STARS" />
 </resultMap>

 <!-- SELECT Statements -->
 <select id="selectDefaultList" resultMap="article">
  select ID, LANG, TITLE, LINK, REGI_DATE, DESC, USERINFO_REF, STARS
   from T_ARTICLELINK,
    (select ARTICLELINK_REF, COUNT(USERINFO_REF) AS STARS
     from T_ART_USER
     group by ARTICLELINK_REF)
   where ID = ARTICLELINK_REF
   order by ID DESC
 </select>
 
sqlmap 파일은 조금 간단해졌지만, 쿼리는 더 복잡해졌다. 서브쿼리에 조인이 포함된 구문이다. 유지보수 관점에서는 복잡한 SQL은 엄청난 교육 비용을 요구하기 때문에 아래와 같은 약간의 성능 향상과 바꾸는 것이 의미가 있을지는 모르겠다. 위 코드는 전적으로 긴급 튜닝용 쿼리로 볼 수 있다.
그렇다면, 또 개선안은 있는가? 비즈니스 룰 즉, 업무 규칙을 정의해 넣는 방식을 생각할 수 있다. 객체지향/CBD를 논하는데 있어서 핵심이라고도 말할 수 있는 부분이다. 위에서 복잡한 쿼리가 생겨난 것은 비즈니스 룰이 SQL에 포함되었기 때문인데 이를 비즈니스 오퍼레이션(메소드)로 포착해내거나 오퍼레이션이 포함할 주요 규칙으로 정의해서 모델로 표현해낸다면 SQL로 묻혀 있는 것과는 분명 다를 것이다.
 
하나의 단점이라면 DB에서 데이터의 중복이 발생할 수 있다는 점과 데이터 입력 작업시 트랜젝션 처리가 요구된다는 점이다. 그렇지만, 모든 것을 만족하는 해법은 없다.
 
이렇게 처리하겠다고 했다면 이 사례에서는 사용자가 추천을 하여 별점을 제공했다면 별점 데이터가 저장될 때, T_ARTICLELINK 테이블에 STARS라는 컬럼에 1을 더해줘야 한다.
 
테이블을 수정하고 테스트 데이터를 입력한 후에 관련 코드를 변경해보겠다.
-- ARTICLE_LINK TABLE
DROP TABLE T_ARTICLELINK IF EXISTS CASCADE;
CREATE TABLE T_ARTICLELINK (
 ID INTEGER,
 LANG CHAR (2),
 TITLE VARCHAR (100),
 LINK VARCHAR (255),
 REGI_DATE TIMESTAMP,
 DESC VARCHAR (1000),
 USERINFO_REF VARCHAR(15),
 STARS INTEGER,
 CONSTRAINT PK_ARTICLELINK PRIMARY KEY (ID),
 CONSTRAINT FK_SUBMITTER FOREIGN KEY (USERINFO_REF) REFERENCES T_USERINFO(ID)
)
 
테이블에 정수 컬럼이 하나 추가되고, 별점 관련한 INSERT 시점에서 아래와 같은 류의 UPDATE 문이 요구된다.
 
UPDATE T_ARTICLELINK SET STARS = STARS + 1 WHERE ID = #id#;
 
이런 처리가 되어 있다면 Sqlmap 파일은 더 단순해진다.
 <resultMap id="article" class="articleLink">
  <result property="id" column="ID" />
  <result property="language" column="LANG" />
  <result property="title" column="TITLE" />
  <result property="link" column="LINK" />
  <result property="regiDate" column="REGI_DATE" />
  <result property="description" column="DESC" />
  <result property="submitter.id" column="USERINFO_REF" />
  <result property="stars" column="STARS" />
 </resultMap>
 <!-- SELECT Statements -->
 <select id="selectDefaultList" resultMap="article">
  select ID, LANG, TITLE, LINK, REGI_DATE, DESC, USERINFO_REF,STARS
   from T_ARTICLELINK
   order by ID DESC
 </select>
 
성능은 SQL이 간단하기 때문에 향상될 것이 뻔하다.


데이터가 작아서 그 차이가 두드러져 보이지 않지만 그래도 차이를 볼 수 있다. 하늘색선과 노란선의 차이가 크지 않음은 시사할 점이다. 성능으로만 논의를 몰고 가면 복잡한 쿼리 탓에 업무 정의가 모호해진다는 점을 간과해선 안된다. 결과만 보면, 과정을 잃게 된다. 고객(협업)이 요구사항을 제대로 줄 수 없는 것은 어떤면에서는 이러한 문제도 그 여파를 제공한다. 두드러지게 드러나지 않도록 구현된 시스템은 작동 방식에 업무 규칙이 들어가 버리니까...

번호 제목 글쓴이 날짜 조회 수
공지 (확인전) [2021.03.12] Eclipse에서 Spring Boot로 JSP사용하기(Gradle) 황제낙엽 2023.12.23 0
공지 [작성중/인프런] 스프링부트 시큐리티 & JWT 강의 황제낙엽 2023.12.20 6
38 [POST] Spring MVC 구조 분석 황제낙엽 2024.01.17 1
37 OAuth 2.0 Resource Server - Spring Security OAuth2.0 황제낙엽 2023.12.27 1
36 [시리즈 강좌] 스프링부트로 웹서비스 구축하기 황제낙엽 2023.07.13 3
35 [Spring3.1.1] ResponseBody 한글깨짐 해결법 황제낙엽 2018.08.08 140
34 [Spring3.1.1][4] RestTemplate 한글 문제 황제낙엽 2018.08.08 89
33 [Spring3.1.1][3] RestTemplate 한글 문제 황제낙엽 2018.08.08 237
32 [Spring3.1.1][2] RestTemplate 한글 문제 황제낙엽 2018.08.08 113
31 [Spring3.1.1][1] RestTemplate 한글 문제 황제낙엽 2018.08.08 683
30 NamedParameterJdbcDaoSupport 몇가지 장점 황제낙엽 2007.11.27 101
29 Spring AOP - Pointcut 황제낙엽 2007.10.02 129
28 <spring:checkbox> tip! 황제낙엽 2007.10.01 378
27 스프링 2와 JPA 시작하기 (한글) 황제낙엽 2007.08.27 142
26 Spring 2.0의 XML확장기능 (3) 황제낙엽 2007.08.15 32
25 Spring 2.0의 XML확장기능 (2) 황제낙엽 2007.08.15 73
24 Spring 2.0의 XML확장기능 (1) 황제낙엽 2007.08.15 33
23 스프링의 구조별 기능 설명 황제낙엽 2007.06.26 42
22 자바지기 스프링 프레임웍 아티클 황제낙엽 2007.06.04 54
21 AOP(Aspect Oriented Programming) 황제낙엽 2007.06.03 39
» Spring MVC 어플리케이션 개발 <12> 간단한 조회 구현 방안 비교 황제낙엽 2007.05.27 31