▼웹 개발자로 살기

서로 다른 프로젝트 다른 톰캣 서버 사용하기


각각의 프로젝트를 각각의 이클립스에서 개발 중.

톰캣도 각각 별개의 서버로 돌리려면

server.xml 에서 아래 내용을 수정하면 된다.


Server port 는 원래 8005 인데 다른 번호로.

Connector port는 원래 8080인데 다른 번호로. (localhost:8080 에서 localhost:8081 이런 식으로 써야 함)

Connector port 8009 인데 8010 으로.


1
2
3
4
5
<Server port="8006" shutdown="SHUTDOWN">
 
<Connector connectionTimeout="20000" port="8081" protocol="HTTP/1.1" redirectPort="8443"/>
 
<Connector port="8010" protocol="AJP/1.3" redirectPort="8443"/>
cs


'999.eclipse' 카테고리의 다른 글

이클립스 새 프로젝트 개발환경 설정하기  (0) 2016.03.07

Java 콤마를 구분하여 배열에 담기


문자열 변수를 콤마 기준으로 잘라야 할 때는 split()을 사용하여

String[] 문자열 배열에 담도록 한다.



1
2
String salesTeam = "홍길동,유관순,강감찬";
String[] salesTeamArray = salesTeam.split(",");
cs




문자열 배열을 문자열 리스트에 담을 때는?


1
2
3
4
5
6
7
String salesTeam = "홍길동,유관순,강감찬";
String[] salesTeamArray = salesTeam.split(",");
 
List<String> salesTeamList = new ArrayList<>();
for (int i = 0; i < salesTeamArray.length; i++) {
    salesTeamList.add(salesTeamArray[i]);
}
cs


myBatis mssql insert 후 바로 시퀀스값 select 하기


MS SQL 테이블 값 인서트하면 자동증가 컬럼 값을 가져오는 방법이 애매해서 찾아보던 중

<selectKey keyProperty> 라는 것을 알게 되어 정리~



<Controller>

1
2
3
4
5
6
7
@RequestMapping(value="/test", method=RequestMethod.GET)
public void testPush(@RequestParam HashMap<String, Object> paramMap, HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    pushService.insertPush(paramMap);
    int pushIdx = (int) paramMap.get("idx");
    
}
cs


HashMap 을 paramMap 이라는 이름의 파라미터로 TABLE_PUSH 에 인서트 하도록 한다


테이블 TABLE_PUSH 의 구조는

IDX(int), SUBJECT(String), MESSAGE(String) 컬럼 3개




<Mapper>

1
2
3
4
5
6
7
8
9
<insert id="insertPush" parameterType="map">
    INSERT INTO TABLE_PUSH (SUBJECT, MESSAGE)
    VALUES (#{subject},
            #{message}            
           );
    <selectKey keyProperty="idx" resultType="Integer" order="AFTER">
        SELECT IDENT_CURRENT('TABLE_PUSH')
    </selectKey>
</insert>
cs


<selectKey keyProperty="컬럼명" resultType="컬럼타입" order="실행시기">

SELECT IDENT_CURRENT('테이블명')



그렇게 하면 <insert> 의 parameterType="map" 으로 썼던 파라미터 paramMap 의 idx 에

현재 자동증가값이 얼마인지 들어가 있게 된다





또는 useGeneratedKeys 를 사용할 수도 있다

1
2
<insert id="insertData" parameterType="DataClass" useGeneratedKeys="true" keyProperty="id">
 
cs


커서치면 아래에 뜨게 하기




<HTML>

1
2
3
4
5
6
7
8
<div class="col-sm-10">
    <div class="col-sm-12">
        <input type="hidden" name="memCode" id="memCode"/>
        <input type="hidden" name="value-keyword" id="value-keyword"/>
        <input type="hidden" name="str" id="str"/>
        <input type="text" class="col-xs-10 col-sm-10" id="memName" name="memName" autocomplete="off" placeholder="회원이름">
    </div>
</div>
cs





아래 스크립트는 .keyup(), .keypress(), .autocomplete() 을 활용한 내용.


참고한 사이트:

http://findfun.tistory.com/284

http://blog.naver.com/junhwen/130158515885

http://javafactory.tistory.com/entry/jQueryEventkeydownkeypresskeyup-%ED%82%A4%EB%B3%B4%EB%93%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8


.keyup()

키보드를 누르고 있다가 뗄 떼 이벤트 발생

누른 키가 뭔지 확인하려면 event 객체 활용

브라우저가 event 객체 정보를 가지고 있으면 jQuery의 .which 속성을 사용해 키보드 키 활인 가능

예) event.which = 13 은 엔터키

실제 텍스트를 잡아내려면 .keyup() 보다는 .keypress()를 사용

영어는 .keypress() 이벤트 사용 가능하지만 한글은 지원하지 않아 .keyup() 사용해야 한다


.keypress()

키보드가 입력 받았을 때 이벤트 발생

키 누름을 감지할 필요가 있을 때

.keydown() 와 비슷하지만 키를 반복적으로 누를 때 다름

키를 계속 누르고 있다면 .keydown() 은 이벤트 한 번 발생, .keypress() 는 문자 찍힐 때마다 이벤트 발생

보조키(shift) 누르면 .keydown() 은 이벤트 발생, .keypress() 는 이벤트 발생하지 않음.

그러나 .keypress() 는 공식적으로 지원하지 않는 함수. 브라우저별, 브라우저 버전별, 플랫폼에 따라 다르게 동잘 할 수 있음.



keydown() - 키보드가 눌러질 때 발생

keypress() - 글자가 입력될 때 발생

keyup() - 키보드가 떼어질 때 발생




<Script>

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
$(document).ready(function(){
    
    //회원명 입력시 회원정보 가져오기
    $("#memName").keyup(function(){
        $("#value-keyword").val($(this).val());
    });
    $("#memName").keypress(function(e){
        if(e.which == 13){
            $(this).blur();
        };
    });
    $("#memName").autocomplete({
        source: function(request, response){
            $.ajax({
                url     : "getMemMobileInfo.do",
                type    : "post",
                data    : {memName: $("#value-keyword").val()},
                success : function(data){
                    response($.map(data.DataList, function(item){
                        return{
                            label: item.STR,                            
                            value: item.STR,
                            memCode: item.MEM_CODE,
                            memTypeInput: item.MEM_TYPE_INPUT,
                            memType: item.MEM_TYPE,
                            pushYn: item.PUSH_YN,
                            deviceInput: item.DEVICE_INPUT,
                            device: item.DEVICE,
                            deviceId: item.DEVICE_ID
                        };
                    }))    
                },
                error    : function(request, status, error){
                    alert("회원 이름으로 정보를 불러오는 중 오류가 발생하였습니다.")
                }
            });
        },
        position: {my: "right top", at: "right bottom"},
        minLENGTH: 2,
        focus: function(event, ui){
            return false;
        },
        select: function(event, ui){
            if (ui.item.pushYn == 'N') {
                alert("푸시알림을 거부한 회원은 선택할 수 없습니다.");
                return false;
            }
            //TBL_MEM_MOBILE_INFO. DEVICE 디바이스구분(A:안드로이드, I:아이폰)
            //TBL_PUSH_NOTICE. OS_TYPE OS(1:안드로이드, 2:아이폰, 3:전체)
            if (ui.item.device == 'A') {
                $("#osType").val("1");
                
            } else if (ui.item.device == 'I' ) {
                $("#osType").val("2");
            };             
            //TBL_MEM_MST. MEM_TYPE 회원종류(0:일반회원, 1:부동산회원)
            //TBL_PUSH_NOTICE. MEM_TYPE 회원종류(1:비중개회원, 2:중개회원, 4:분양회원, 7:전체)
            //5:비중개회원(개별), 6:중개회원(개별)
            if (ui.item.memType == '0') {
                $("#memType").val('5');
            } else if (ui.item.memType == '1') {
                $("#memType").val('6');
            };
            $("#memCode").val(ui.item.memCode);
            $("#device").val(ui.item.device);
            $("#osTypeInput").val(ui.item.deviceInput);
            $("#memTypeInput").val(ui.item.memTypeInput);
            $("#memName").blur();
        },
        open: function(e, ui){
            $(this).removeClass("ui-corner-all").addClass("ui-corner-top");
        },
        closefunction(){
            $(this).removeClass("ui-corner-top ui-autocomplete-loading").addClass("ui-corner-all");
        }
    });
})
cs


HttpClient, HttpPost, HttpResponse, HttpEntity 사용하기


HttpCliend

HttpPost

HttpResponse

HttpEntity


NameValuePair


공부해야게따ㅠㅠ



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://localhost:8080/testLatLngData");
httpPost.addHeader("Content-Type""application/x-www-form-urlencoded");
 
List<NameValuePair> testParam = new ArrayList<>();
testParam.add(new BasicNameValuePair("lat", ( "37.469997210138416" )));
testParam.add(new BasicNameValuePair("lng", ( "126.9273415126953" )));
 
httpPost.setEntity(new UrlEncodedFormEntity(testParam, "UTF-8"));
 
HttpResponse resp = httpClient.execute(httpPost);
 
HttpEntity respEntity = resp.getEntity();
 
Object obj = JSONValue.parse(new InputStreamReader(respEntity.getContent()));
 
//결과처리
//JSONObject jsonObject = (JSONObject) obj;
cs


MultiValueMap, RestTemplate, HttpEntity 이용하기

문자발송 코드 짜다가.. 까먹을까봐 정리해둠.


MultiValueMap parameters 에 파라미터 값들을 담고

(multipart/form-data 가 있는 경우는 MultiValueMap<String, Object> 로 하면 되는지는 테스트해봐야함)


HttpHeaders headers 에 Content-Type 정보와 클라이언트키값(api key)값을 담는다.

Content-Type 은 두 가지로 나뉘는데 RestTemplate을 사용하면 알아서 정해진다고 한다.

1
2
headers.add("Content-Type""application/x-www-form-urlencoded; charset=UTF-8"); //전부다 String형일 때. RestTemplate 쓰면 생략가능
headers.add("Content-Type""multipart/form-data; boundary=----WebKitFormBoundary8UhbmC4vAvBxT6z3"); //multipart/form-data 있는 경우 사용. RestTemplate 쓰면 생략 가능
cs



위 parameters 와 headers 를 HttpEntity<> request 에 담는다.

위에서 MultiValueMap을 <String, String>으로 선언했기 때문에 HttpEntity<MultiValueMap<String, String>> 이다.


RestTemplate.postForObject() 을 실행할 때 URI, request, String.class 변수로 넘긴다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
MultiValueMap<StringString> parameters = new LinkedMultiValueMap<>();
 
parameters.add("send_phone""12341234");
parameters.add("dest_phone""01012345678");
parameters.add("msg_body""단문 문자 테스트");
parameters.add("subject""문자 제목");
 
HttpHeaders headers = new HttpHeaders();
//headers.add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); //전부다 String형일 때. RestTemplate 때문에 생략가능
//headers.add("Content-Type", "multipart/form-data; boundary=----WebKitFormB~~3"); //multipart/form-data 있는 경우 사용. RestTemplate 때문에 생략 가능
headers.add("x-waple-authorization""API키값");
 
HttpEntity<MultiValueMap<StringString>> request = new HttpEntity<>(parameters, headers);
 
RestTemplate rest = new RestTemplate();
String result = rest.postForObject(new URI("http://api.openapi.io/ppurio/1/message/mms/API스토어ID"), request, String.class);
//결과 {“result_code”:”200”,”cmid”:”20130314163439459”}
 
// 결과값에 따른 처리
JSONObject feedback = (JSONObject) new JSONParser().parse(result);
String resultCode = (String) feedback.get("result_code");
 
cs




참고 : 

http://www.apistore.co.kr/api/apiView.do?service_seq=151

http://blog.saltfactory.net/java/post-multipart-form-data-using-resttemplate-in-spring.html

자바 AES 256 암호화 복호화

아파치에서 제공하는 패키지를 통해 자바 AES 256 암호화, 복호화를 해보기로 한다.



1.


먼저 Apache Commons Codec 패키지를 이용하기 위해서는 

Apache Commons Codec 패키지를 직접 jar 파일을 라이브러리에 수동으로 추가하거나,

Maven 의 경우 pom.xml 에 dependency 를 추가해줘야 한다.


수동으로 라이브러리를 추가하려면

http://blog.naver.com/slimcdp/220495115002

어떤 분이 잘 설명해놓으신 위 블로그 포스트를 참고하면 되고, (아래 AES256Util.java 소스나 테스트 소스도 이 포스트를 참고하였다 )


메이븐의 경우 아래 내용을 참고하면 된다.


https://mvnrepository.com/artifact/commons-codec/commons-codec

MVN Repository 사이트에서 검색하면 여러가지 버전이 있는데


1.10으로 해보기로.




위 소스코드를 그대로 복사해서 pom.xml 에 붙여넣기 하면 된다.





2. AES256Util.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.test.api.utils;
 
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
 
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
 
import org.apache.commons.codec.binary.Base64;
 
/*
Copyright 회사명 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
 
public class AES256Util {
    private String iv;
    private Key keySpec;
    
    public AES256Util(String key) throws UnsupportedEncodingException {
        this.iv = key.substring(016);
        
        byte[] keyBytes = new byte[16];
        byte[] b = key.getBytes("UTF-8");
        int len = b.length;
        if (len > keyBytes.length) {
            len = keyBytes.length;
        }
        System.arraycopy(b, 0, keyBytes, 0, len);
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        
        this.keySpec = keySpec;
    }
    
 
    // 암호화
    public String aesEncode(String str) throws java.io.UnsupportedEncodingException, 
                                                    NoSuchAlgorithmException, 
                                                    NoSuchPaddingException, 
                                                    InvalidKeyException, 
                                                    InvalidAlgorithmParameterException, 
                                                    IllegalBlockSizeException, 
                                                    BadPaddingException {
        Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
        c.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes()));
 
        byte[] encrypted = c.doFinal(str.getBytes("UTF-8"));
        String enStr = new String(Base64.encodeBase64(encrypted));
 
        return enStr;
    }
 
    //복호화
    public String aesDecode(String str) throws java.io.UnsupportedEncodingException,
                                                        NoSuchAlgorithmException,
                                                        NoSuchPaddingException, 
                                                        InvalidKeyException, 
                                                        InvalidAlgorithmParameterException,
                                                        IllegalBlockSizeException, 
                                                        BadPaddingException {
        Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
        c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes("UTF-8")));
 
        byte[] byteStr = Base64.decodeBase64(str.getBytes());
 
        return new String(c.doFinal(byteStr),"UTF-8");
    }
 
}
 
cs


3. 암호화, 복호화, URL Encoding, Decoding 테스트

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
String key = "aes256-test-key!!";    //key는 16자 이상
AES256Util aes256 = new AES256Util(key);
URLCodec codec = new URLCodec();
 
String encLoginidx = codec.encode(aes256.aesEncode(loginidx)); 
String decLoginidx = aes256.aesDecode(codec.decode(encLoginidx));
 
logger.debug("### loginidx:"+loginidx); //MEM201605090243
logger.debug("### encLoginidx:"+encLoginidx); //encoding 전 K8OcI65S+lX9MjEKN5EMdQ== / 후 K8OcI65S%2BlX9MjEKN5EMdQ%3D%3D
logger.debug("### decLoginidx:"+decLoginidx); //MEM201605090243
 
String encLat = codec.encode(aes256.aesEncode(""+Lat)); 
String decLat = aes256.aesDecode(codec.decode(encLat));
 
logger.debug("### Lat:"+Lat);    //37.71248872643193
logger.debug("### encLat:"+encLat);    //encoding 전 0FTRN5NS7FrhNsJ4GEI4Hc62CLzuTx89JUX+2Z4PvqE= / 후 0FTRN5NS7FrhNsJ4GEI4Hc62CLzuTx89JUX%2B2Z4PvqE%3D
logger.debug("### decLat:"+decLat);    //37.71248872643193
 
String encLng = codec.encode(aes256.aesEncode(""+Lng)); 
String decLng = aes256.aesDecode(codec.decode(encLng));
 
logger.debug("### Lng:"+Lng);    //127.26688771630859
logger.debug("### encLng:"+encLng);    //encoding 전 OVDlF0whDEW7MxGbJvk84Jajx4ve0tikK4YXj+Z8y6Q= / 후 OVDlF0whDEW7MxGbJvk84Jajx4ve0tikK4YXj%2BZ8y6Q%3D
logger.debug("### decLng:"+decLng);    //127.26688771630859
 
cs




그 외에, 암호화된 데이터로 넘겨받았는지 아닌지 체크하기 (암호화 된 값이라면 String형이므로 Double 로 형변환 실패할 것)

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 암호화 된 좌표인지 확인하기 위한 임시 변수
 * 실제 복호화 할 필요가 있는지
 * 복호화 할 필요가 있으면 true 
 */
boolean aesAvail = false;
String aesLat = (String)param.get("neLat");
try{
    Double.parseDouble(aesLat);
    aesAvail = false;
catch(Exception e){
    aesAvail = true;
}

cs


스프링 PageableDefault 사용하기

 

어렵다 JPA ㅠㅠ...

 

잘은 모르겠지만 소스 파악하고 수정해야될 게 있어 인터넷에서 검색한거 까먹을까봐 정리해본다

 

 

Pageable 을 쓸 때, 사이즈는 얼마, 정렬은 어떻게 등을 기본으로 잡아주기 위해 @PageableDefault 를 사용한다

 

근데 화면단에서 동적으로 10개씩 보여주기, 20개씩 보여주기 등 하고 싶으면 어떻게 해야 하나?

 

 

 

결론부터 말하자면...

 

http://localhost:8080/persons?page=1&size=20

 

이런식으로 파라미터 page, size, sort 를 쓰면 된다.

 

 

 

 

그리고 화면단에서 현재 페이지 숫자와 로우수도 바로 알 수 있다.

${result.size} - 현재 페이지 로우 수 (ex, 10, 20, 30, ...)

${result.name} - 현재 페이지 (ex. 0, 1, 2, ...)

 

 

<JAVA>

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping("/test.do")
public String findAllTest(@PageableDefault(size=20, sort="name",direction = Sort.Direction.ASC) Pageable pageable,
                                     @ModelAttribute("searchVO") SearchVO searchVO,
                                     ModelMap model) {
    
    model.addAttribute("result", testService.getTestList(pageable, searchVO));
    return "/test.jsp";
}
cs

 

 

<Script>

1
2
3
4
5
6
7
8
9
//Display rows 수 변경
function changeRowsPerPage(rowsPerPage, currentPage){    
    location.href="${rc.contextPath}/test.do?"
                                                            +"page="+currentPage
                                                            +"&size="+rowsPerPage
                                                            +"&searchType="+$("#searchType").val()
                                                            +"&searchValue="+$("#searchValue").val()
                                                            +"&checkAll="+$("#checkAll").val();
}
cs

 

<HTML>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div class="btn-group" role="group">
    <button data-toggle="dropdown" class="btn btn-xs btn-primary">
    ${result.size}개씩 보기<i class="ace-icon fa fa-angle-down icon-on-right bigger-110"></i>
    </button>
    <ul class="dropdown-menu dropdown-menu-right dropdown-125 dropdown-lighter dropdown-close dropdown-caret">
        <li <c:if test="${result.size eq '10'}">class="active disabled"</c:if> >
            <a href="javascript:;" onclick="changeRowsPerPage('10', '${result.number}');" <c:if test="${result.size eq '10'}">class="blue"</c:if> >
            <i class="ace-icon fa fa-caret-right bigger-110 <c:if test="${result.size ne '10'}">invisible</c:if> "> </i>
            10개씩 보기
            </a>
        </li>
        <li <c:if test="${result.size eq '20'}">class="active disabled"</c:if> >
            <a href="javascript:;" onclick="changeRowsPerPage('20', '${result.number}');" <c:if test="${result.size eq '20'}">class="blue"</c:if> >
            <i class="ace-icon fa fa-caret-right bigger-110 <c:if test="${result.size ne '20'}">invisible</c:if> "> </i>
            20개씩 보기</a>
        </li>
        <li <c:if test="${result.size eq '30'}">class="active disabled"</c:if> >
            <a href="javascript:;" onclick="changeRowsPerPage('30', '${result.number}')" <c:if test="${result.size eq '30'}">class="blue"</c:if> >
            <i class="ace-icon fa fa-caret-right bigger-110 <c:if test="${result.size ne '30'}">invisible</c:if> "> </i>
            30개씩 보기
            </a>
        </li>
    </ul>
</div> 
cs

 

 

참고:

Java Code Examples for org.springframework.data.web.PageableDefault

http://www.programcreek.com/java-api-examples/index.php?api=org.springframework.data.web.PageableDefault

 

http://millky.com/@origoni/post/1171?language=ko_kr

 

Spring Data JPA - Reference Documentation

http://docs.spring.io/spring-data/jpa/docs/1.7.2.RELEASE/reference/html/#_handlermethodargumentresolvers_for_pageable_and_sort

 

MSSQL PIVOT 사용해서 행을 열로 바꾸기

 

1
2
3
4
5
6
7
8
9
SELECT DATEPART(WEEKDAY, REG_DATE) WEEKDAY
     , SUBSTRING(CONVERT(VARCHAR(10), REG_DATE, 108), 12) HOURS
     , COUNT(1) totalMemUvCnt
     , SUM(VISITCNT) totalUvCnt
     , SUM( CASE WHEN DEVICE_TYPE = 'WEB' THEN 1 ELSE 0 END ) webMemUvCnt
  FROM TBL_LOG_UV 
 WHERE REG_DATE >= '2016-06-01'
   AND REG_DATE < DATEADD(DAY, 1'2016-06-30')
 GROUP BY SUBSTRING(CONVERT(VARCHAR(10), REG_DATE, 108), 12), DATEPART(WEEKDAY, REG_DATE)
cs

 

 

WEEKDAY HOURS totalMemUvCnt totalUvCnt webMemUvCnt
1 00 122 351 45
1 01 101 199 43
1 02 72 115 21
1 03 41 62 12
1 04 33 57 12
1 05 22 51 5
1 06 28 41 7
1 07 41 84 13
1 08 59 154 19
1 09 68 188 20
1 10 113 288 40
1 11 95 407 33
1 12 108 322 40
1 13 113 261 40
1 14 88 342 32
1 15 132 290 57
1 16 176 380 56
1 17 121 404 41
1 18 137 279 49
1 19 109 194 38
1 20 119 240 43
1 21 118 198 43
1 22 162 269 53
1 23 146 216 35
2 00 156 356 43
2 01 106 221 46
2 02 71 155 27
2 03 60 119 25
2 04 30 68 14
2 05 25 43 10
2 06 42 84 16
2 07 57 96 9
2 08 80 1169 24
2 09 125 821 56
2 10 175 1650 99
2 11 182 932 97
2 12 146 533 67
2 13 172 925 99
2 14 163 539 84
2 15 175 464 74
2 16 186 489 90
2 17 158 379 74
2 18 150 378 75
2 19 131 455 48
2 20 133 282 55
2 21 162 290 62
2 22 152 278 56
2 23 144 287 49

... 이런식으로 WEEKDAY는 1부터 7까지 (월~일)

WEEKDAY 한개마다 HOURS는  24개씩 반복(00시~23시)

 

 

여기서 WEEKDAY 1~7를 말머리 행으로 올리고 싶었다.

예를 들면 이렇게..

  totalMemUvCnt_
HOURS totalMemUvCnt_ totalMemUvCnt_ totalMemUvCnt_ totalMemUvCnt_ totalMemUvCnt_ totalMemUvCnt_ totalMemUvCnt_
00 122 156 146 209 233 203 144
01 101 106 105 174 181 148 128
02 72 71 57 117 131 89 71
03 41 60 56 75 85 58 77
04 33 30 24 66 70 48 33
05 22 25 28 62 48 33 43
06 28 42 35 80 67 40 42
07 41 57 55 104 96 72 48
08 59 80 63 147 111 83 72
09 68 125 117 178 159 130 95
10 113 175 176 241 227 295 132
11 95 182 211 264 228 267 149
12 108 146 176 256 215 179 109
13 113 172 189 211 248 356 99
14 88 163 215 236 253 307 100
15 132 175 228 259 255 230 115
16 176 186 220 265 281 197 108
17 121 158 364 224 256 173 103
18 137 150 263 221 217 153 84
19 109 131 185 174 193 140 102
20 119 133 215 207 196 142 110
21 118 162 228 204 240 124 152
22 162 152 208 268 222 167 126
23 146 144 192 215 223 125 112

 

 

1
2
3
4
5
6
7
8
SELECT *
  FROM (
               --메인내용
       ) A
PIVOT (
        SUM(totalMemUvCnt)
        FOR WEEKDAY IN ([1], [2], [3], [4], [5], [6], [7])
      ) AS  B
cs

 

 

HOURS 1 2 3 4 5 6 7
00 122 156 146 209 233 203 144
01 101 106 105 174 181 148 128
02 72 71 57 117 131 89 71
03 41 60 56 75 85 58 77
04 33 30 24 66 70 48 33
05 22 25 28 62 48 33 43
06 28 42 35 80 67 40 42
07 41 57 55 104 96 72 48
08 59 80 63 147 111 83 72
09 68 125 117 178 159 130 95
10 113 175 176 241 227 295 132
11 95 182 211 264 228 267 149
12 108 146 176 256 215 179 109
13 113 172 189 211 248 356 99
14 88 163 215 236 253 307 100
15 132 175 228 259 255 230 115
16 176 186 220 265 281 197 108
17 121 158 364 224 256 173 103
18 137 150 263 221 217 153 84
19 109 131 185 174 193 140 102
20 119 133 215 207 196 142 110
21 118 162 228 204 240 124 152
22 162 152 208 268 222 167 126
23 146 144 192 215 223 125 112

 

 

이렇게 했더니... WEEKDAY(1~7) 마다 각 시간별로 totalMemUvCnt 컬럼 SUM 한 것이 의도한 대로 위치에 들어갔다.

 

그런데... 다 풀린 줄 알았던 것도 잠시.

totalUvCnt 를 SUM 해서 피벗 B 옆에 또 피벗 C를 붙여 넣으려고 했는데

이미 피벗 B에서 WEEKDAY 컬럼을 써버려 사라지고..

 

WEEKDAY컬럼을 여러개 해야 하나.... 그럼 WEEKDAYA, WEEKDAYB, ... 이런식으로 하면 피벗을 계속 추가할 수는 있는데.

1
2
3
4
5
6
7
8
PIVOT (
        SUM(totalMemUvCnt)
        FOR WEEKDAYA IN ([1], [2], [3], [4], [5], [6], [7])
      ) AS  B
PIVOT (
        SUM(totalUvCnt)
        FOR WEEKDAYB IN ([8], [9], [10], [11], [12], [13], [14])
      ) AS  C
cs

 

뭔가 비효율적이다...

 

CASE문을 쓰거나

WITH절 등을 활용하는 게 나을 것 같다.

 

참고

http://ntalbs.github.io/2015/sql-transpose/

인터넷 익스플로러에서만 개체가 'includes' 속성이나 메서드를 지원하지 않습니다 에러 발생

크롬에선 잘 되던게... 익스플로러에서만 작동하지 않아 콘솔을 보니.

 

개체가 'includes' 속성이나 메서드를 지원하지 않습니다. 라는 에러가...?

 

 

 

흠...

변수가 object 여서 문젠가 싶어, 문자열로도 테스트를 해봤는데. 어떻게 지지고 볶아도 해결이 되지 않아 검색을 좀 더 해보니...

 

 

요구 사항

Microsoft Edge(Edge 브라우저)에서 지원됩니다. 스토어 앱(Windows 10의 Microsoft Edge)에서도 지원됩니다. 버전 정보를 참조하십시오.

Quirks, Internet Explorer 6 표준, Internet Explorer 7 표준, Internet Explorer 8 표준, Internet Explorer 9 표준, Internet Explorer 10 표준, Internet Explorer 11 표준과 같은 문서 모드에서는 지원되지 않습니다. Windows 8.1에서는 지원되지 않습니다.

 

이건 뭐... 인터넷 익스플로러에서는 거의 안된다는 말과 다름없는.......

ㅎ.........

 

 

대체할 방법은 없는가 싶어 구글링.

그리고 스택 오버플로우에는 이미 나같은 사람들을 위한 답변들이ㅋ

 

참고해서,

.includes() 대신에 .indexOf 로 바꾸니 크롬, 익스플로러 구분없이 잘 된다.

 

 

에러가 발생하는 자바스크립트 소스

1
2
3
4
if (opt.value.includes(search)) {
    sel.selectedIndex = j;
    break;
}
cs

 

 

보완한 자바스크립트 소스

1
2
3
4
if (opt.value.indexOf(search) != -1) {
    sel.selectedIndex = j;
    break;
}
cs

 

 

 

참고

https://msdn.microsoft.com/ko-kr/library/dn858228(v=vs.94).aspx

+ Recent posts