[JAVA]method ()V not found 오류

[JAVA] method ()V not found 오류

자바에서 method ()V not found 에러가 나는 경우.

ex) com.packagename.test.TestController:method ()V not found

스택오버플로우를 찾아보니 생성자(constructor)에서 오류가 발생한 경우라고 했다.

오늘 본 현상은 jsp 파일에서 오류가 발생했다.

jsp 상단 java 영역에서 사용한 특정 클래스명으로 method ()V not found 에러가 났다.

로컬 환경 테스트는 이상 없었다.

문제가 발생한 운영 환경에서도 해당 클래스는 다른 여러 jsp에서 잘 사용하고 있는 정상적인 클래스였다.

오류 발생한 jsp를 FTP로 새로 업로드하여 기존 파일에 덮어씌우니 잘 되었다.

아마도 WAS가 해당 jsp를 컴파일할 때 일시적인 오류가 발생한 것으로 생각된다.

로컬에서 특별한 문제가 없어보인다면 클래스 파일이나 관련 jsp를 다시 패치하고 WAS 재기동이 답인듯.

참고사이트 : https://stackoverflow.com/questions/13998202/method-initv-not-found

[javascript] html 태그 내의 어트리뷰트 값 가져오기 (html get tag attribute) (getAttribute)

[javascript] html 태그 내의 어트리뷰트 값 가져오기 (html get tag attribute) (getAttribute)

html 태그 내의 어트리뷰트 값을 가져오기 위해서는 getAttribute 함수를 쓰면 된다.

id, name 등 예약된 어트리뷰트가 아닌 사용자가 임의로 지어낸 어트리뷰트 명도 가능하다. (예를 들면 myTagName)

html 태그 내의 어트리뷰트 값 가져오기

document.getElementById(“태그아이디”).getAttribute(“값을 가져오고자 하는 어트리뷰트명”);

ex) document.getElementById(“targetDiv”).getAttribute(“myTagName”);

예제 코드

<html>
<head>
<script>
window.onload = function() {
    var value = document.getElementById(“targetDiv”).getAttribute(“myTagName”);
    alert(“myTagName value : ” + value);
}
</script>
</head>
<body>
    <div id=”targetDiv” myTagName=”myTagValue”></div>
</body>
</html>

IE와 크롬 둘 다 동작한다.

 

[CSS] HTML CSS ul li 점 삭제 (점 없애기)

[CSS] HTML CSS ul li 점 삭제 (점 없애기)

1. ul li 점 삭제

<style>

ul {
    list-style: none;
 }

</style>

2. ul li 점 삭제 + 들여쓰기 삭제
<style>

ul {
    list-style: none;
    padding-left: 0px;
 }

</style>

참고사이트 : https://biju.tistory.com/256

[Java] InvocationTargetException 의 getCause 와 getTargetException

[Java] InvocationTargetException 의 getCause 와 getTargetException

InvocationTargetException  에 대하여 아는대로 간략하게 적어본다.

틀린 점이나 보완할 점을 댓글로 적어주시기 바란다.

InvocationTargetException 은 자바에서 invoke 를 수행했을 때 오류가 발생하는 경우다.

invoke란 스트링 문자열을 통해 특정 클래스 내부의 메서드를 실행시키는 것으로, 이러한 기법을 리플렉션이라고 한다.

InvocationTargetException 은 스택 트레이스를 찍어봤자(ex.printStackTrace();) 의미있는 정보를 얻을 수 없다.

겉 껍데기에 불과한 InvocationTargetException 의 스택 트레이스만 찍히기 때문이다.

따라서 cause(ex.getCause())의 스택 트레이스를 찍어야 유의미한 정보를 얻을 수 있다.

ex.getCause().printStackTrace();

그런데 이 때 cause가 null인 경우가 있다. (ex.getCause() == null)

100% 그러한지는 모르겠는데, 경험적으로는 invoke한 메서드 내에서 Exception 이 발생한 경우가 아니라 Error 가 발생한 경우다.

이 경우 cause가 null 이다.

참고로 Exception 과 Error 는 똑같이 Throwable 객체를 상속받은 친구들이다.

invoke한 메서드 내에서 Error가 발생하는 사례를 들자면, 일부 클래스 파일 패치가 누락되어 특정 필드(특정 클래스의 멤버변수)를 찾을 수 없는 경우다. 이 때 Error의 getMessage()를 찍어보면 Unresolved compilation problem: [필드명] cannot be resolved or is not a field 이다.

정리하면 InvocationTargetException 의 cause 는 null 이 될 수 있으므로, getCause() 의 스택 트레이스를 찍으려면 반드시 null 체크를 해야 한다.

참고로 getTargetException() 메서드가 있는데 Exception에는 없고 InvocationTargetException 에만 존재하는 메서드다.

따라서 Throwable targetThrowable = ((InvocationTargetException) ex).getTargetException(); 이렇게 Exception 을 캐스팅(형 변환)해야 하여 getTargetException() 메서드를 사용할 수 있다. getTargetException() 은 내부적으로 멤버변수인 terget 을 리턴하는 메서드로, getCause() 와 동일하다고 생각하면 된다.

글로 쓴 내용을 소스코드로 정리하면 다음과 같다.

// InvocationTargetException 은 자바에서 invoke 를 수행했을 때 오류가 발생하는 경우
if (ex != null && ex instanceof InvocationTargetException) {

    // invoke 대상에서 Exception 이 아닌 Error 가 발생한 경우 cause 가 null 인 InvocationTargetException 넘어옴
    if (ex.getCause() != null) {

        System.err.println(ex.getMessage());
        ex.getCause().printStackTrace();
    }

}

또는

// InvocationTargetException 은 자바에서 invoke 를 수행했을 때 오류가 발생하는 경우
if (ex != null && ex instanceof InvocationTargetException) {

    // invoke 대상에서 Exception 이 아닌 Error 가 발생한 경우 cause 가 null 인 InvocationTargetException 넘어옴

​    Throwable targetThrowable = ((InvocationTargetException) ex).getTargetException();
    if (targetThrowable != null) {

        System.err.println(targetThrowable.getMessage());
        targetThrowable.printStackTrace();
    }

}

[Python] error: could not find a version that satisfies the requirement tensorflow (from versions: none)

[Python] error: could not find a version that satisfies the requirement tensorflow (from versions: none)

파이썬에서 텐서플로우를 설치하려고 할 때 error: could not find a version that satisfies the requirement tensorflow (from versions: none) 라는 메시지가 나오는 문제.

다시 말해 cmd 에서 pip install tensorflow 라고 쳤을 때 위 메시지가 나오는 경우 해결방법이다.

몇 시간 동안 인터넷에 나오는대로 아나콘다도 설치해보고 별 짓 다 해봤지만… 다 안된다.

일단 cmd 에서 python –version (또는 python3 –version) 을 쳐서 버전을 확인해본다.

Python 3.6.5 라고 나오지 않고 높은 버전일 경우 (예를 들어 3.8.0 일 경우) 언인스톨하자.

(1) 제어판에 들어가서 파이썬, 아나콘다 등 지금 설치되어있는 파이썬을 모두 지운다. 필자의 경우 파이썬 2를 빼고 모두 지웠다. 파이썬 2는 안지웠음.

(2) 언인스톨이 끝나면 윈도우 재시작 후, 파이썬 웹사이트 들어가서 정확히 Python 3.6.5 버전을 새로 다운받아 설치한다.

* 다운로드 페이지 주소 : https://www.python.org/downloads/release/python-365/

* 윈도우 64비트 설치파일 주소 : https://www.python.org/ftp/python/3.6.5/python-3.6.5-amd64.exe

(3) cmd 들어가서 pip install tensorflow 치면 아래처럼 정상적으로 설치될 것이다.

 

[Python] 파이썬 2, 3 동시사용(같이 사용)

[Python] 파이썬 2, 3 동시사용(같이 사용)

한 컴퓨터에서 파이썬 2와 파이썬 3을 동시사용(같이 사용)하는 방법을 소개한다.

1. 파이썬 2, 3 설치

우선 파이썬 2, 3을 설치한다.

설치 방법은 https://blog.naver.com/bb_/221716543567 를 참고하면 된다.

필자는 파이썬 2, 파이썬 3 순서로 설치하였으며, 기본 경로를 바꾸지 않고 설치하였다.

 

 

2. 파이썬 2, 3 설치경로 알아내기

원래 파이썬은 cmd 에서 python 이라고 입력하면 실행된다.

이제는 python2 라고 입력하면 파이썬 2를 실행, python3 이라고 입력하면 파이썬 3이 실행되도록 해보겠다.

그러려면 파이썬 2의 경로와 파이썬 3의 경로를 알아야 한다.

필자의 경우, 파이썬 2의 경로는 C:\Python27 이었다.

파이썬 3의 경로는 C:\Users\gendev\AppData\Local\Programs\Python\Python38 이었다.

파이썬 2의 기본 경로는 C:\Python27 이 맞다.

파이썬 3 경로를 잘 모르겠으면 C:\Users 안에서 찾아본다.

보통 파이썬 3의 기본 경로는 C:\Users\사용자명\AppData\Local\Programs\Python\Python38 이다.

 

 

3. 파이썬 2 링크파일 생성

cmd 를 관리자 권한으로 실행한다.

mklink C:\Windows\python2.exe [파이썬 2 설치경로] 를 입력한다.

예륻 들어, mklink C:\Windows\python2.exe C:\Python27\python.exe 라고 입력한다.

이어서 python2 –version 이라고 입력하면 파이썬 2의 자세한 버전이 나와야 한다.

이제 python2 라고 입력하면 파이썬 2가 실행된다.

 

 

4. 파이썬 3 링크파일 생성

cmd 를 관리자 권한으로 실행한다.

mklink C:\Windows\python3.exe [파이썬 3 설치경로] 를 입력한다.

예를 들어, mklink C:\Windows\python3.exe C:\Users\gendev\AppData\Local\Programs\Python\Python38\python.exe 라고 입력한다.

이어서 python3 –version 이라고 입력하면 파이썬 3의 자세한 버전이 나와야 한다.

이제 python3 이라고 입력하면 파이썬 3이 실행된다.

 

5. 파이썬 2의 pip 링크파일 생성

파이썬 2 뿐만 아니라 pip 도 링크파일을 생성하자. pip 은 파이썬 패키지 관리자로, Pip Installs Packages 의 약자다.

cmd 를 관리자 권한으로 실행한다.

mklink C:\Windows\pip2.exe [파이썬 2 설치경로]\Scripts\pip.exe 를 입력한다.

예를 들어, mklink C:\Windows\pip2.exe C:\Python27\Scripts\pip.exe 라고 입력한다.

이어서 pip2 –version 이라고 입력하면 파이썬 2 pip의 세부 버전이 나와야 한다.

 

 

6. 파이썬 3의 pip 링크파일 생성 (생략)

파이썬 3의 pip 링크파일 생성은 생략한다.

cmd 에서 pip3 –version 이라고 입력했을 때 파이썬 3 pip의 세부 버전이 잘 나오기 때문이다.

 

 

7. 파이썬 2, 3 설치

​이제 파이썬 2와 파이썬 3를 사용하면 된다.

파이썬 2는 python2 명령어로, 파이썬 3는 python3 명령어로 사용하면 된다.

참고페이지 : https://http2.tistory.com/19

[Pyhon] 파이썬 윈도우 설치

[Pyhon] 파이썬 윈도우 설치

1.  https://www.python.org/downloads/windows/ 페이지 접속

 

파이썬 3을 설치할지 파이썬 2를 설치할지에 따라, [Latest Python 3 Release – Python 3.8.0] 또는 [Latest Python 2 Release – Python 2.7.17] 를 클릭한다.

참고로 파이썬 3과 파이썬 2는 호환되지 않는다. 즉, 파이썬 3로 짠 프로그램이 파이썬 2에서 실행되지 않는 경우가 많다.

2. 하단의 Files 항목에서 설치파일 다운로드

[Latest Python 3 Release – Python 3.8.0] 을 클릭한 경우, 스크롤을 내리면 하단에 Files 항목이 있다.

여기서 64비트 컴퓨터일 경우, [Windows x86-64 executable installer] 를 클릭하여 설치파일을 다운로드 받는다.

만약 32비트 컴퓨터라면 [Windows x86 executable installer] 를 클릭한다.

3. 다운받은 설치파일(python-3.8.0-amd64.exe)을 더블클릭하여 실행한다.

4. 하단의 [Install launcher for all users (recommended)] 를 체크, [Add Python 3.8 to PATH] 를 체크한다.

이어서 [Install Now] 를 클릭한다.

 

5. 기다린다.

6. [Disable path length limit] 를 클릭한다. 만약 아래 화면이 나오지 않는다면 무시해도 좋다.

 

7. [Close] 를 클릭한다.


모두를 위한 머신러닝/딥러닝 강의

모두를 위한 머신러닝/딥러닝 강의

홍콩과기대 김성훈 교수님의 강의링크입니다.

https://hunkim.github.io/ml/

프로그래밍 알려주는 블로거

프로그래밍 알려주는 블로거

저는 프로그래밍 알려주는 블로거입니다.

[Windows] 윈도우 10 방화벽 인바운드 특정 ip만 허용

[Windows] 윈도우 10 방화벽 인바운드 특정 ip만 허용

윈도우 10 로컬에서 웹서버 또는 와스를 띄웠을 때, 외부에서 아이피 주소를 치면 누구나 접근할 수 있다.

이 때 특정 ip 만 외부에서 접속할 수 있도록 허용해주는 방법이다.

1. 우선 웹서버 또는 와스를 80 포트로 띄운다.

다시 말해, 웹브라우저에서 http://현재컴퓨터아이피/ 로 접속했을 때 어떤 화면이 나오도록 조치한다.

현재 컴퓨터 아이피를 알아내기 위해서는 cmd 에서 ipconfig 를 쳐도 되고, 잘 모르겠다면 http://ipconfig.co.kr/ 로 접속하면 확인할 수 있다.

웹서버 또는 와스가 정상적으로 기동됐다면, 다른 PC 에서 http://현재컴퓨터아이피/ 로 접속했을 때 화면이 잘 보일 것이다.

예를 들어 모바일에서 http://현재컴퓨터아이피/ 로 접속해보자. 잘 떠야 정상이다.

2. 다음으로 모든 ip에 대해서 접속할 수 없게 차단해야 한다.

[제어판] – [모든 제어판 항목]- [Windows Defender 방화벽]

좌측의 [고급 설정] 클릭

방화벽 윈도우가 뜬다.

방화벽 윈도우 좌측의 [인바운드 규칙] 클릭

목록이 나오면, 그룹 컬럼을 눌러서 정렬시킨다.

그룹이 비어있는 모든 항목을 선택하고, 마우스 우클릭 – [규칙 사용 안 함]을 선택한다.

Java(TM) Platform SE binary 라는 이름의 인바운드 규칙 모두와, OpenJDK Platform binary 라는 이름의 인바운드 규칙 모두 [규칙 사용 안 함] 처리한다.

이제 방화벽 규칙이 다 막혔기 때문에 다른 PC 에서 접속해도 접속이 안될 것이다.

예를 들어 모바일에서 http://현재컴퓨터아이피/ 로 접속했을 때, 접속이 안되어야 한다.

접속이 된다면 현재 컴퓨터와 동일한 와이파이를 쓴 경우이다. 와이파이가 아닌 데이터 사용을 하면 모바일에서 접속 불가할 것이다.

만약 그래도 모바일에서 접속이 된다면, 어떤 방화벽 규칙이 아직 접속을 허용하고 있는 것이다.

어떤 규칙인지는 잘 몰라도 해당 규칙을 찾아 [규칙 사용 안 함] 적용해서, 다른 모바일이나 PC에서 접속 불가능하게 만들어야 한다.

3. 특정 ip만 접근 허용하도록 설정하자.

 

방화벽 윈도우 좌측의 [인바운드 규칙] 클릭

우측의 [새 규칙…] 을 클릭한다.

 

[사용자 지정]을 선택하고 [다음] 클릭한다.

 

[모든 프로그램] 선택하고 [다음] 클릭한다.

 

프로토콜 종류 [모두] 선택하고 [다음] 클릭한다.

 

하단의 [이 규칙이 적용되는 원격 IP 주소] 항목을 [다음 IP 주소] 선택하고 [추가] 클릭한다.

접근 허용하고자 하는 ip 주소를 입력하고 [확인] 버튼 클릭한다.

원하는 ip를 모두 입력했다면 [다음] 버튼 클릭한다.

예를 들어 모바일에서 ipconfig.co.kr 을 들어가서 특정 ip가 나오면, 그 값을 기입한다.

해당 모바일 기기에서만 접속 가능하게 하는 것이다.

(참고로 모바일의 ip는 유동적으로 바뀌기 때문에 시간이 지나면 안될 수도 있다.)

 

[연결 허용] 선택하고 [다음] 클릭한다.

 

[도메인], [개인], [공용] 모두 체크한 상태에서 [다음] 클릭한다.

적당한 이름을 짓고 [마침] 클릭한다.

이제 특정 ip 에서만 웹서버 또는 와스를 접근할 수 있다.

[VBA] MEDIANIFS, MODEIFS

[VBA] MEDIANIFS, MODEIFS

Attribute VB_Name = “BB_VBA_API_191106”
‘주어진 조건으로 중간값을 구한다.
‘사용법 : BB_MEDIANIFS(계산대상, [조건범위1], [조건값1], [조건범위2], [조건값2], [조건범위3], [조건값3] …)
Function BB_MEDIANIFS(ParamArray paramArr())
    ‘에러 발생시 BB_MEDIANIFS_ERROR 레이블로 이동(GoTo)
    On Error GoTo BB_MEDIANIFS_ERROR
   
    ‘조건에 맞는 범위를 배열 형태로 가져온다. 만약 오류 발생시 오류 메시지를 리턴한다.
    Dim resultArr
    resultArr = getValuesIfs(paramArr)
   
    If TypeName(resultArr) = “String” Then
        BB_MEDIANIFS = resultArr
        Exit Function
    End If
   
    ‘배열 쏘팅
    resultArr = sortArray(resultArr)
   
    ‘배열 사이즈 가져오기
    Dim arrCount
    arrCount = getArraySize(resultArr)
   
    ‘중간값을 산출한다.
    Dim firstIndex
   
    If arrCount Mod 2 = 0 Then
        ‘배열의 개수가 짝수이면 중간에 위치한 2개의 값을 평균낸다.
        firstIndex = (Int(arrCount) / 2) – 1
       
        BB_MEDIANIFS = (CDbl(resultArr(firstIndex)) + CDbl(resultArr(firstIndex + 1))) / 2
        Exit Function
    Else
        ‘배열의 개수가 홀수이면 중간에 위치한 1개의 값을 가져온다.
        firstIndex = ((Int(arrCount) – 1) / 2)
       
        BB_MEDIANIFS = resultArr(firstIndex)
        Exit Function
    End If
   
BB_MEDIANIFS_ERROR:
    BB_MEDIANIFS = “[오류] 에러코드 ” & Err.Number & ” 발생 : ” & Err.Description & “”

End Function

‘주어진 조건으로 최빈값을 구한다.
‘사용법 : BB_MODEIFS(계산대상, [조건범위1], [조건값1], [조건범위2], [조건값2], [조건범위3], [조건값3] …)
Function BB_MODEIFS(ParamArray paramArr())
    ‘에러 발생시 BB_MODEIFS_ERROR 레이블로 이동(GoTo)
    On Error GoTo BB_MODEIFS_ERROR
   
    ‘조건에 맞는 범위를 배열 형태로 가져온다. 만약 오류 발생시 오류 메시지를 리턴한다.
    Dim resultArr
    resultArr = getValuesIfs(paramArr)
   
    If TypeName(resultArr) = “String” Then
        BB_MODEIFS = resultArr
        Exit Function
    End If
   
    ‘배열 사이즈 가져오기
    Dim arrCount
    arrCount = getArraySize(resultArr)
   
    Dim lastIndex
    lastIndex = arrCount – 1
   
    ‘최빈값을 구한다.
   
    Dim maxKey
    maxKey = “”
   
    Dim maxCount
    maxCount = 0
   
   
    Dim MyCollection As New Collection

    For i = 0 To lastIndex
       
        Dim oneKey
        oneKey = resultArr(i) & “”
       
        ‘오류 방지를 위해 쉼표를 제거한다.
        oneKey = Replace(oneKey, “,”, “”)
       
        Dim newCount
        newCount = 0
       
        Dim oldCount
        oldCount = 0
       
        On Error Resume Next
        oldCount = MyCollection.Item(oneKey)
        On Error GoTo 0
       
        If checkIsNumber(oldCount) = True Then
            newCount = Int(oldCount) + 1
        Else
            newCount = 1
        End If
       
        On Error Resume Next
        MyCollection.Add Item:=newCount, Key:=oneKey
        On Error GoTo 0
       
        If maxCount < newCount Then
            maxKey = oneKey & “”
            maxCount = newCount
           
        ElseIf maxCount = newCount Then
            If maxKey <> oneKey And InStr(1, maxKey, ” , ” & oneKey, vbTextCompare) < 1 And InStr(1, maxKey, oneKey & ” , “, vbTextCompare) < 1 Then
                maxKey = maxKey & ” , ” & oneKey
            End If
        End If
       
    Next i
   
   
    If maxKey = “” Then
        BB_MODEIFS = “결과없음”
    Else
        BB_MODEIFS = maxKey & “(” & maxCount & “회)”
    End If
   
    Exit Function
   
BB_MODEIFS_ERROR:
    BB_MODEIFS = “[오류] 에러코드 ” & Err.Number & ” 발생 : ” & Err.Description & “”

End Function

‘getValuesIfs : 조건에 맞는 범위를 배열 형태로 가져온다. 만약 오류 발생시 오류 메시지를 리턴한다.
Private Function getValuesIfs(ByVal paramArr)
    ‘에러 발생시 getValuesIfs_error 레이블로 이동(GoTo)
    On Error GoTo getValuesIfs_error
   
    Dim paramCount
    paramCount = 0
   
    Dim param
    For Each param In paramArr
        paramCount = paramCount + 1
    Next

    If paramCount < 1 Then
        getValuesIfs = “[오류] 인자는 1개 이상이어야 합니다.”
        Exit Function
    End If
   
    If paramCount Mod 2 = 0 Then
        getValuesIfs = “[오류] 인자는 홀수 개여야 합니다.”
        Exit Function
    End If
   
   
   
   
    ‘leftAddr, rightAddr 변수에 문자열을 저장한다. $가 포함되어 있다면 제거한다.
    ‘ex) 기존값 “$A$1:$D$10” 이라면 각각 “A1”, “D10” 이 된다.
    ‘ex) 기존값 “$A$1” 이라면 각각 “A1”, “A1” 이 된다.
    Dim addr
    addr = paramArr(0).Address & “”
    addr = Replace(addr, “$”, “”)
   
    Dim leftAddr, leftColNum, leftRowNum
    Dim rightAddr, rightColNum, rightRowNum
   
    Dim colonIdx
    colonIdx = InStr(addr, “:”)

    If colonIdx > 0 Then
        ‘콜론이 존재하는 경우(다중범위)
        leftAddr = Mid(addr, 1, colonIdx – 1)
        leftColNum = Range(leftAddr).Column
        leftRowNum = Range(leftAddr).Row
       
        rightAddr = Mid(addr, colonIdx + 1, Len(addr) – colonIdx)
        rightColNum = Range(rightAddr).Column
        rightRowNum = Range(rightAddr).Row
       
    Else
        ‘콜론이 존재하지 않는 경우(단일범위)
        leftAddr = addr
        leftColNum = Range(leftAddr).Column
        leftRowNum = Range(leftAddr).Row
       
        rightAddr = addr
        rightColNum = Range(rightAddr).Column
        rightRowNum = Range(rightAddr).Row
    End If
   
   
    If leftColNum <> rightColNum Then
        getValuesIfs = “[오류] 1번째 인자의 열 2개가 일치하지 않습니다.”
        Exit Function
    End If
   
   
   
   
    ‘행 계산
    Dim minRowNum
    minRowNum = leftRowNum
   
    Dim maxRowNum
    maxRowNum = rightRowNum
   
    If minRowNum > maxRowNum Then
        getValuesIfs = “[오류] 1번째 인자의 행 2개의 순서를 바꿔 입력하여 주십시오.”
        Exit Function
    End If
   
    ‘계산대상(첫번째 인자) 행 개수 세기
    Dim rowCount
    If (rightRowNum – leftRowNum) > (rightColNum – leftColNum) Then
        rowCount = rightRowNum – leftRowNum + 1
    Else
        rowCount = rightColNum – leftColNum + 1
    End If
   
   
   
   
    ‘조건개수
    Dim condCount
    condCount = (paramCount – 1) / 2
   
    ‘조건범위배열
    Dim condRangeArr
    ReDim condRangeArr(condCount)
   
    For i = 0 To condCount – 1
        Dim paramIdx
        paramIdx = 2 * i + 1
       
        condRangeArr(i) = paramArr(paramIdx).Address & “”
       
        ‘달러를 제거한다.
        condRangeArr(i) = Replace(condRangeArr(i), “$”, “”)
       
        Dim tmpIdx
        tmpIdx = InStr(condRangeArr(i), “:”)
       
        If tmpIdx > 0 Then
            ‘콜론이 존재하는 경우(다중범위)
            Dim tmpLeftCol
            Dim tmpLeftRow
            tmpLeftCol = Range(Mid(condRangeArr(i), 1, tmpIdx – 1)).Column
            tmpLeftRow = Range(Mid(condRangeArr(i), 1, tmpIdx – 1)).Row
           
            Dim tmpRightCol
            Dim tmpRightRow
            tmpRightCol = Range(Mid(condRangeArr(i), tmpIdx + 1, Len(condRangeArr(i)) – tmpIdx)).Column
            tmpRightRow = Range(Mid(condRangeArr(i), tmpIdx + 1, Len(condRangeArr(i)) – tmpIdx)).Row
           
            If tmpLeftCol <> tmpRightCol Then
                getValuesIfs = “[오류] ” & (paramIdx + 1) & “번째 인자의 열 2개가 일치하지 않습니다.”
                Exit Function
            End If
           
            If (tmpRightRow – tmpLeftRow + 1) <> rowCount Then
                getValuesIfs = “[오류] 1번째 인자의 행 개수와 ” & (paramIdx + 1) & “번째 인자의 행 개수가 일치하지 않습니다.”
                Exit Function
            End If
           
            If (tmpLeftRow <> minRowNum) Or (tmpRightRow <> maxRowNum) Then
                getValuesIfs = “[오류] 1번째 인자의 행 범위와 ” & (paramIdx + 1) & “번째 인자의 행 범위가 일치하지 않습니다.”
                Exit Function
            End If
           
            condRangeArr(i) = tmpLeftCol
           
        Else
            ‘콜론이 존재하지 않는 경우(단일범위)
            condRangeArr(i) = Range(condRangeArr(i)).Column
        End If
       
    Next i
   
    ‘조건값배열
    Dim condValArr
    ReDim condValArr(condCount)
   
    For i = 0 To condCount – 1
        condValArr(i) = paramArr(2 * i + 2)
    Next i
   
   
   
   
    ‘결과배열
    Dim resultArr
    ReDim resultArr(rowCount)
   
    ‘인덱스
    Dim resultIndex
    resultIndex = 0
   
    For r = leftRowNum To rightRowNum
        For c = leftColNum To rightColNum
            targetValue = Cells(r, c).Value
           
            ‘대상이 숫자가 아닌 경우 무시한다.
            Dim bIsNumber
            bIsNumber = checkIsNumber(targetValue)
           
            Dim bAllCondValid
            bAllCondValid = True
           
            If bIsNumber = True Then
                For k = 0 To condCount – 1
                    If (Cells(r, condRangeArr(k)).Value & “”) <> (condValArr(k) & “”) Then
                        bAllCondValid = False
                    End If
                Next k
            Else
                bAllCondValid = False
            End If
           
            If bAllCondValid = True Then
                ‘MsgBox (bAllCondValid)
                ‘모든 조건 충족하면 결과배열에 담는다.
                resultArr(resultIndex) = targetValue & “”
                ‘MsgBox (resultIndex & ” ; ” + resultArr(resultIndex))
                resultIndex = resultIndex + 1
               
            End If
        Next c
    Next r
   
   
    Dim newCount
    newCount = resultIndex
   
    Dim newArr
    ReDim newArr(newCount)
   
    For i = 0 To (newCount – 1)
        newArr(i) = resultArr(i)
    Next i
   
    getValuesIfs = newArr
    Exit Function
   
   
getValuesIfs_error:
    getValuesIfs = “[오류] 에러코드 ” & Err.Number & ” 발생 : ” & Err.Description & “”

End Function

‘checkIsNumber : 값이 숫자인지 체크. 마이너스, 마침표, 쉼표까지 숫자로 친다.
Private Function checkIsNumber(str)

    str = str & “”

    Dim le
    le = Len(str)

    If le < 1 Then
        checkIsNumber = False
        Exit Function
    End If

   
    Dim ch
    For i = 1 To le
        ch = Mid(str, i, 1) & “”
        If ch <> “0” And ch <> “1” And ch <> “2” And ch <> “3” And ch <> “4” And ch <> “5” And ch <> “6” And ch <> “7” And ch <> “8” And ch <> “9” And ch <> “-” And ch <> “.” And ch <> “,” Then
            checkIsNumber = False
            Exit Function
        End If
    Next i
   
    checkIsNumber = True
   
End Function

‘sortArray : 배열을 쏘팅한다.
Private Function sortArray(arr)

    Dim arrCount
    arrCount = getArraySize(arr)

    Dim arrLastIdx
    arrLastIdx = arrCount – 1
   
    For i = 0 To arrLastIdx
        For k = i + 1 To arrLastIdx
            ‘정렬(쏘팅) 조건은 숫자로 한다.
            If CDbl(arr(i)) > CDbl(arr(k)) Then
                Dim tempVal
                tempVal = arr(i) & “”
                arr(i) = arr(k) & “”
                arr(k) = tempVal & “”
            End If
        Next k
    Next i
   
    ‘For i = 0 To arrLastIdx
    ‘    MsgBox (i & ” : ” & arr(i))
    ‘Next i
   
    sortArray = arr
End Function

‘getArraySize : 배열 사이즈 가져오기
Private Function getArraySize(arr)
    getArraySize = UBound(arr) – LBound(arr)
    ‘MsgBox (“getArraySize : ” & getArraySize)
End Function
 

[Xcode] 맥에서 ipa 파일을 아이폰 기기로 옮기는 방법 (Xcode 이용)

[Xcode] 맥에서 ipa 파일을 아이폰 기기로 옮기는 방법 (Xcode 이용)

1. Siging(Debug)에 정상적인 개발 인증서(Development)가 등록되어 있는 경우
아이폰 기기를 맥에 USB로 연결한다.
Xcode 좌측 상단의 Device 를 망치아이콘인 Generic iOS Device 로 맞춘다.
Xcode 좌측 상단의 재생 버튼(Build and then run the current scheme)을 클릭해서 진행한다.

2. Siging(Debug)에 개발 인증서(Development)가 만료되었거나 없는 경우
Siging(Debug)에 배포 인증서(Distribution)를 넣고, 1번 항목대로 진행한다.
다만 이 경우에는 Xcode 쪽에 디버그 불가능하다는 경고 메시지가 한 번 표시될 수 있다.
실제로 개발 인증서 대신 배포 인증서를 사용하므로 디버그 불가능하다.

3. 따로 ipa 파일을 갖고 있고 특정 아이폰 기기에 옮기고 싶은 경우
아이폰 기기를 맥에 USB로 연결한다.
Xcode 상단 Window – Devices and Simulators 클릭하고 하단 앱 영역에 원하는 ipa 파일을 드래그 앤 드롭으로 끌어다 놓는다.
참고로 해당 영역에서 아이폰 기기 내의 앱 삭제도 가능하다.

[javascript] js 스크롤 맨 위로 / js 스크롤 맨 아래로

[javascript] js 스크롤 맨 위로 / js 스크롤 맨 아래로

<script>

// 스크롤 맨 위로

document.documentElement.scrollTop = 0;

</script>

위 코드가 동작하지 않을 때 아래 코드를 사용한다.

<script>

// 스크롤 맨 위로

document.body.scrollTop = 0;

</script>

<script>

// 스크롤 맨 아래로

document.body.scrollTop = document.body.scrollHeight;

</script>

[javascript] iframe height 100% auto resize (아이프레임 높이 100% 자동조절)

[javascript] iframe height 100% auto resize (아이프레임 높이 100% 자동조절)

iframe 크기를 내용에 맞춰 동적 조절하는 방법이다.

iframe width는 100% 로 맞추면 되지만, height 는 100%로 맞춘다고 자동 조절되지 않는다.

아래처럼 onload 어트리뷰트에 setFrameHeight() 함수를 할당한다.

<script>
function setFrameHeight(){
    if (ContentUrl.document != null) {
        document.all.ContentUrl.style.height = ContentUrl.document.body.scrollHeight + 30 + “px”;
    }
}
</script>

<body>
    <iframe width=”100%” height=”100%” id=”ContentUrl” name=”ContentUrl” src=http://google.com/ scrolling=”auto” frameborder=”0″ onload=”setFrameHeight()” style=”border:0px; min-height: 250px;”></iframe>
</body>

[Mac] “프로그램명”은(는) 확인되지 않은 개발자가 배포했기 때문에 열 수 없습니다.

[Mac] “프로그램명”은(는) 확인되지 않은 개발자가 배포했기 때문에 열 수 없습니다.

맥에서 프로그램을 실행했을 때 “프로그램명”은(는) 확인되지 않은 개발자가 배포했기 때문에 열 수 없습니다. 라는 메시지가 뜰 경우.

맥 좌측 상단의 사과 모양 아이콘 클릭 – [시스템 환경설정…] – [보안 및 개인 정보 보호] 클릭 – 하단의 다음에서 다운로드한 앱 허용 항목에서 [확인 없이 열기] 버튼 클릭하면, 방금 전 실행하려고 시도했던 프로그램이 실행된다.

[JAVA] 바이트(Byte)를 용량 단위에 맞게 환산하기

[JAVA] 바이트(Byte)를 용량 단위에 맞게 환산하기

/**
     * 바이트(Byte)를 용량 단위에 맞게 환산하기
     *
     * @param strSize
     * @return
     */

    private String getSizeString(String strSize) {
        if (strSize == null || strSize.length() == 0) {
            return “0B”;
        }

        long nSize = 0;
        String resultSize = “”;

        try {
            nSize = Long.parseLong(strSize);

            DecimalFormat df = new DecimalFormat(“#,###.##”);

            String[] arrUnit = {“B”, “KB”, “MB”, “GB”, “TB”, “PB”};
            long nUnit = 1024;
            long nSection = 1;
            for (int i=0; i<6; i++) {
                if (nSize < nSection * nUnit) {
                    resultSize = df.format(nSize * 1.0 / nSection) + arrUnit[i];
                    break;
                }

                nSection *= nUnit;
            }

        } catch (NumberFormatException e) {
            resultSize = strSize + “B”;

        } catch (Exception e) {
            resultSize = strSize + “B”;
        }

        if (resultSize == null || resultSize.length() == 0) {
            resultSize = strSize + “B”;
        }

        return resultSize;
    }

[Jennifer] 제니퍼 쓰레드(Thread) kill 불가능한 경우

[Jennifer] 제니퍼 쓰레드(Thread) kill 불가능한 경우

제니퍼에서 특정 쓰레드(예를 들면 불기둥이 올라온 쓰레드)를 kill 했을 때 죽지 않는 경우가 있다.

문의결과 이러한 경우는 소켓을 읽고 있는 경우(socket read 상태)라고 한다.

소켓이 읽고 있는 경우라도 강제로 쓰레드를 kill 하기 위해서는 enable_thread_kill_with_socket_close 옵션을 On 하면 된다고 한다.

적용은 해당 옵션 변경 후 WAS 재기동해줘야 한다.

<적용 방법>

1. 제니퍼 우측 상단의 톱니바퀴 아이콘 클릭

2. [모니터링 대상] – [에이전트 고급 옵션] 항목 클릭. 이후 옵션 적용하고자 하는 인스턴스 선택한다.

 

3. 옵션 중에서 enable_thread_kill_with_socket_close 항목을 찾아 [사용자 정의 설정] On 으로 변경. 화면 우측의 [저장] 버튼 클릭.

4. 적용은 해당 인스턴스 WAS 재기동 후 적용된다.

참고로 제니퍼는 일정 시간 이상 지연된 쓰레드를 자동으로 kill 하도록 처리할 수 있는데, socket read 상태인 쓰레드는 이에 해당하지 않는다. 위 옵션을 적용하더라도 자동 kill은 불가능하다는 이야기다.

socket read 상태인 쓰레드는 위 옵션을 적용해야만 kill할 수 있으며, 오직 수동으로만 kill 할 수 있다고 한다.

[javascript] get select option value / get select option text

​[javascript] get select option value / get select option text

자바스크립트 셀렉트박스(콤보박스)의 option값 가져오기.

(1) id로 가져오기

var selObj = document.getElementById(“elementId”);

var value = selObj.options[selObj.selectedIndex].value;

var text = selObj.options[selObj.selectedIndex].text;

(2) name으로 가져오기

var selObj = document.getElementsByName(“elementName”)[0];

var value = selObj.options[selObj.selectedIndex].value;

var text = selObj.options[selObj.selectedIndex].text;

[JAVA] txt 파일 인코딩 확인

[JAVA] txt 파일 인코딩 확인

자바에서 txt 파일 인코딩을 확인하기 위해서는 juniversalchardet 라는 라이브러리를 사용하면 된다고 한다.

본 포스트에서는 라이브러리 없이 단순하게 인코딩을 확인했다.

txt 파일의 일반적인 인코딩은 MS949이며, 본 포스트에서는 MS949 와 UTF-8 이렇게 두 가지 경우의 수만 고려했다.

방법은 먼저 MS949 로 txt 파일을 읽다가, 대체문자(65533)를 발견하면 UTF-8 파일로 판단하여 처음부터 다시 UTF-8로 읽어오는 것이다.

아래 코드에서 readFileMS949orUTF8 메서드를 찾아보면 된다.

참고로 65533 이란 유니코드 대체문자라 부른다. 잘못된 디코더를 적용했을 때, 유효하지 않은 바이트 시퀀스(손상된 바이트 시퀀스)를 대체하기 위한 문자다. 문자열이 깨져 표시되는 물음표를 생각하면 된다.

package com.bb.test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

public class TestClass {

    public static void main(String[] args) {
        
        new TestClass().main1();
    }
        
    public void main1() {
        
        try {
            File inputFile1 = new File(“C:\\test\\ms949.txt”);
            File inputFile2 = new File(“C:\\test\\utf8.txt”);
            
            // 우선 MS949로 읽는다. 그러다 UTF-8 대체문자(65533)를 발견하면 UTF-8로 읽는다.
            ArrayList<String> fileContent1 = readFileMS949orUTF8(inputFile1);
            ArrayList<String> fileContent2 = readFileMS949orUTF8(inputFile2);
            
            System.out.println(fileContent1);
            System.out.println(fileContent2);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    
    /**
     * 우선 MS949로 읽는다. 그러다 UTF-8 대체문자(65533)를 발견하면 UTF-8로 읽는다.
     *
     * @param file
     * @return
     * @throws IOException
     * @throws Exception
     */

    private ArrayList<String> readFileMS949orUTF8(File file) throws IOException, Exception {
        if (file == null || !file.exists()) {
            return null;
        }

        boolean bUTF8 = false;
        ArrayList<String> resultList = null;

        FileInputStream fileInputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;

        try {
            fileInputStream = new FileInputStream(file);
            inputStreamReader = new InputStreamReader(fileInputStream, “MS949”);
            bufferedReader = new BufferedReader(inputStreamReader);

            // 우선 MS949로 읽는다. 그러다 UTF-8 대체문자(65533)를 발견하면 UTF-8로 읽는다.
            int len = 0;
            String oneLine = null;
            while ((oneLine = bufferedReader.readLine()) != null) {
                if (resultList == null) {
                    resultList = new ArrayList<String>();
                }
                
                len = oneLine.length();
                for (int k=0; k<len; k++) {
                    if (((int) oneLine.charAt(k)) == 65533) {
                        bUTF8 = true;
                        break;
                    }
                }
                
                if (bUTF8) {
                    break;
                }

                resultList.add(oneLine);
            }

        } catch (IOException e) {
            throw e;

        } catch (Exception e) {
            throw e;

        } finally {
            close(bufferedReader);
            close(inputStreamReader);
            close(fileInputStream);
        }
        

        // 우선 MS949로 읽는다. 그러다 UTF-8 대체문자(65533)를 발견하면 UTF-8로 읽는다.
        if (bUTF8) {
            return readFile(file, “UTF-8”);
        }

        return resultList;
    }
    
    
    private ArrayList<String> readFile(File file, String charset) throws IOException, Exception {
        if (file == null || !file.exists()) {
            return null;
        }

        ArrayList<String> resultList = null;

        FileInputStream fileInputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;

        try {
            fileInputStream = new FileInputStream(file);
            inputStreamReader = new InputStreamReader(fileInputStream, charset);
            bufferedReader = new BufferedReader(inputStreamReader);

            String oneLine = null;
            while ((oneLine = bufferedReader.readLine()) != null) {
                if (resultList == null) {
                    resultList = new ArrayList<String>();
                }

                resultList.add(oneLine);
            }

        } catch (IOException e) {
            throw e;

        } catch (Exception e) {
            throw e;

        } finally {
            close(bufferedReader);
            close(inputStreamReader);
            close(fileInputStream);
        }

        return resultList;
    }
    
    
    private boolean writeFile(File file, ArrayList<String> stringList, String charset, boolean bAppend) throws IOException, Exception {
        if (file == null) {
            return false;
        }
        
        boolean bWrite = false;

        FileOutputStream fileOutputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        BufferedWriter bufferedWriter = null;

        try {
            fileOutputStream = new FileOutputStream(file, bAppend);
            outputStreamWriter = new OutputStreamWriter(fileOutputStream, charset);
            bufferedWriter = new BufferedWriter(outputStreamWriter);

            if (stringList != null && stringList.size() > 0) {
                String oneLine = null;

                int lineCount = stringList.size();
                int lastIndex = lineCount – 1;

                for (int i = 0; i < lineCount; i++) {
                    oneLine = stringList.get(i);

                    bufferedWriter.write(oneLine, 0, oneLine.length());
                    if (i < lastIndex) {
                        bufferedWriter.newLine();
                    }
                }
            }

            bWrite = true;

        } catch (IOException e) {
            throw e;

        } catch (Exception e) {
            throw e;

        } finally {
            close(bufferedWriter);
            close(outputStreamWriter);
            close(fileOutputStream);
        }

        return bWrite;
    }
    
    
    private static void close(BufferedWriter bufferedWriter) {
        try {
            if (bufferedWriter != null) {
                bufferedWriter.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            bufferedWriter = null;
        }
    }
        
        
    private static void close(OutputStreamWriter outputStreamWriter) {
        try {
            if (outputStreamWriter != null) {
                outputStreamWriter.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            outputStreamWriter = null;
        }
    }
    
    
    private static void close(FileOutputStream fileOutputStream) {
        try {
            if (fileOutputStream != null) {
                fileOutputStream.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            fileOutputStream = null;
        }
    }
    
    
    private static void close(FileInputStream fileInputStream) {
        try {
            if (fileInputStream != null) {
                fileInputStream.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            fileInputStream = null;
        }
    }

    
    private static void close(InputStreamReader inputStreamReader) {
        try {
            if (inputStreamReader != null) {
                inputStreamReader.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            inputStreamReader = null;
        }
    }
    
    
    private static void close(BufferedReader bufferedReader) {

        try {
            if (bufferedReader != null) {
                bufferedReader.close();
            }
        } catch (Exception e) {
            // 무시
            
        } finally {
            bufferedReader = null;
        }
    }
}

[JAVA] 자바 프로퍼티 파일 읽기 (java properties 파일 읽기)

[JAVA] 자바 프로퍼티 파일 읽기 (java properties 파일 읽기)

자바에서 프로퍼티 읽기는 아래 코드를 사용하면 된다.

InputStream is = new FileInputStream(new File(“프로퍼티_파일경로.properties”));
Properties props = new Properties();

props.load(is);

is.close();

다만 InputStream 을 사용하기에 위 코드 그대로 사용해서는 안되고 try ~ catch 를 적용해야 한다.

전체 코드는 다음과 같다.

 

package com.bb.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

public class MainClass {

    public static void main(String[] args) {
        
        MainClass mainClass = new MainClass();
        
        String filePath = “db.properties”;
        Properties dbProp = mainClass.readProperties(filePath);
        
        System.out.println(dbProp);
    }
    
    
    public Properties readProperties(String filePath) {
        InputStream is = null;
        Properties props = null;
        File file = null;
        
        try {
            file = new File(filePath);
            if (!file.exists()) {
                System.err.println(“File not found : “ + file.getAbsolutePath());
                return null;
            }
            
            is = new FileInputStream(file);
            
            props = new Properties();
            props.load(is);
            close(is);
            
        } catch (Exception e) {
            e.printStackTrace();
            
        } finally {
            close(is);
        }
        
        return props;
    }
    
    
    public static void close(InputStream is) {
        try {
            if (is != null) {
                is.close();
            }
            
        } catch (Exception e) {
            // ignore
            
        } finally {
            is = null;
        }
    }
}

 

[JAVA] 일정시간 이후에 자바 쓰레드 강제종료 시키기 (java thread interrupt)

[JAVA] 일정시간 이후에 자바 쓰레드 강제종료 시키기 (java thread interrupt)

 

심각하게 오래 진행되는 프로세스가 있을 경우, 특정 Thread를 중지시켜야 하는 경우가 있다.

이 경우 killerThread 라는 걸 만들어서, 특정 Thread 의 소요시간이 오래 걸릴 경우 interrupt 해주면 된다.

참고로 killerThread와 특정 Thread는 서로가 서로를 겨누고 있어야 한다.

즉, 일정 시간이 지나면 killerThread가 특정 Thread를 interrupt 해야 하지만,

특정 Thread가 진행되는게 빠를 경우에는 특정 Thread 종료시 killerThread를 제거해야 한다.

package com.bb.test;

public class MainClass {

    public static void main(String[] args) {
        
        System.out.println(“메서드 호출하기 전”);
        
        MainClass mainClass = new MainClass();
        mainClass.doSomething();
        
        System.out.println(“메서드 호출 이후”);
    }

    
    public void doSomething() {
        
        System.out.println(“프로세스 시작”);
        
        // 현재 Thread 를 변수에 저장
        final Thread currentThread = Thread.currentThread();
        
        // 일정시간 지나면 현재 Thread 를 종료
        Thread killerThread = new Thread() {
            
            @Override
            public void run() {
                try {
                    // 1분 후 종료
                    Thread.sleep(60000);
                    
                } catch (InterruptedException e) {
                    // 킬러 Thread 종료(killerThread.interrupt())하면 이곳에 도달
                    System.out.println(“프로세스 종료”);
                    return;
                    
                } catch (Exception e) {
                    // 무시
                }
                
                try {
                    // 일정시간이 지나면 이곳에 도달
                    System.out.println(“시간초과로 인해 종료합니다.”);
                    
                    // 현재 Thread 를 종료
                    currentThread.interrupt();
                    
                } catch (Exception e) {
                    // 무시
                }
            }
        };
            
            
        try {
            // 일정시간 지나면 현재 Thread 를 종료
            killerThread.start();
            
            int limit = 999999;
            for (int i=0; i<limit; i++) {
                System.out.println(“진행중… (“ + (i+1) + ” / “ + limit + “)”);
                Thread.sleep(1000);
            }
            
        } catch (InterruptedException e) {
            System.out.println(“프로세스가 너무 오래 실행되고 있습니다. 프로세스를 종료합니다.”);
            
        } catch (Exception e) {
            e.printStackTrace();
            
        } finally {
            // 킬러 Thread 종료
            try {
                killerThread.interrupt();
            } catch (Exception e) {
                // 무시
            }
        }
    }

[JAVA] SimpleLogger 클래스

[JAVA] SimpleLogger 클래스

로그 쓰기용 자바 클래스.

주요 기능은 현재 위치에 log 폴더를 만들고 그 안에 로그 파일을 쓰는 것이다.

클래스 하나만 갖다 놓으면 로그를 기록할 수 있게 간단한 형태로 만들어보았다.

———-

package com.bb.simpleset

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Calendar;

public class SimpleLogger {
 
 
 /**
  * Exception 객체의 trace 를 로그로 쓴다.
  * @param paramException
  */
 public static void printLog(Exception paramException) {
  try {
   printLog(convExceptionToString(paramException));
   
  } catch (Exception e) {
   // 로그 쓰기 실패
   e.printStackTrace();
  }
 }
 
 
 /**
  * 문자열을 로그로 쓴다.
  * @param str
  */
 public static void printLog(String str) {
  
  try {
   if (str == null) {
    str = “”;
   }
   
   ArrayList<String> strList = new ArrayList<String>();
   strList.add(str);
   
   printLog(strList);
  
  } catch (Exception e) {
   // 로그 쓰기 실패
   e.printStackTrace();
  }
 }
 
 
 /**
  * 문자열 리스트를 로그로 쓴다.
  * @param stringList
  */
 public static void printLog(ArrayList<String> stringList) {
  
  try {
   String filePath = createLogFolder();
   writeLogFile(filePath, stringList, true);
   
  } catch (Exception e) {
   // 로그 쓰기 실패
   e.printStackTrace();
  }
 }
 
 
 /**
  * 폴더를 생성한다.
  *
  * @return
  * @throws Exception
  */
 private static synchronized String createLogFolder() throws Exception {
  String currentDate = getCurrentDate();
  String year = currentDate.substring(0, 4);
  String month = currentDate.substring(4, 6);
  
  String folderPath = “log/” + year + “/” + month;
  File dirObj = new File(folderPath);
  if (!dirObj.exists()) {
   dirObj.mkdirs();
  }
  
  String fileName = currentDate + “.log”;
  String filePath = folderPath + “/” + fileName;
  File logFile = new File(filePath);
  if (!logFile.exists()) {
   logFile.createNewFile();
  }
  
  return filePath;
 }
   
   
 /**
  * 로그 쓰기 공통
  *
  * @param filePath
  * @param stringList
  * @param bAppend
  * @return
  * @throws IOException
  * @throws Exception
  */
 private static synchronized boolean writeLogFile(String filePath, ArrayList<String> stringList, boolean bAppend) throws IOException, Exception {
        if (filePath == null || filePath.length() == 0) {
            return false;
        }

        File file = new File(filePath);

        boolean bWrite = false;

        FileOutputStream fileOutputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        BufferedWriter bufferedWriter = null;

        try {
            fileOutputStream = new FileOutputStream(file, bAppend);
            outputStreamWriter = new OutputStreamWriter(fileOutputStream, “UTF-8”);
            bufferedWriter = new BufferedWriter(outputStreamWriter);

            if (stringList != null && stringList.size() > 0) {
                String oneLine = null;

                String currentDateTime = getCurrentDateTimeWithSlash();
                bufferedWriter.write(“[” + currentDateTime + “] “);
                System.out.print(“[” + currentDateTime + “] “);

                int lineCount = stringList.size();
               
                for (int i=0; i<lineCount; i++) {
                    oneLine = stringList.get(i);
                    if (oneLine == null) {
                     oneLine = “”;
                    }
                   
                    bufferedWriter.write(oneLine, 0, oneLine.length());
                    bufferedWriter.newLine();
                    System.out.println(oneLine);
                }
            }

            bWrite = true;

        } catch (IOException e) {
            throw e;

        } catch (Exception e) {
            throw e;

        } finally {
            close(bufferedWriter);
            close(outputStreamWriter);
            close(fileOutputStream);
        }

        return bWrite;
    }
   
   
    private static void close(BufferedWriter bufferedWriter) {
        try {
            if (bufferedWriter != null) {
                bufferedWriter.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            bufferedWriter = null;
        }
    }
   
   
    private static void close(OutputStreamWriter outputStreamWriter) {
        try {
            if (outputStreamWriter != null) {
                outputStreamWriter.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            outputStreamWriter = null;
        }
    }
   
   
    private static void close(FileOutputStream fileOutputStream) {
        try {
            if (fileOutputStream != null) {
                fileOutputStream.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            fileOutputStream = null;
        }
    }
   
   
    /**
     * Exception 객체의 stackTrace 를 얻어서 문자열로 변환하여 리턴한다.
     *
     * @param e
     * @return
     */
    public static String convExceptionToString(Exception e) {
  if (e == null) {
   return “”;
  }
  
  StringBuffer buff = new StringBuffer();
  
        // java.lang.ArithmeticException: / by zero
  buff.append(e.getClass().getName() + “: ” + e.getMessage() + “\n”);
  
        StackTraceElement[] trace = e.getStackTrace();
        if (trace != null && trace.length > 0) {
         StackTraceElement stack = null;
            for (int i=0; i<trace.length; i++) {
                stack = trace[i];
                String str = stack.toString();
       
                // at Tester.aa(Tester.java:23)
                // at Tester.main(Tester.java:8)
                buff.append(“at ” + str + “\n”);   
            }
        }

        return buff.toString();
    }
   
   
    /**
     * 현재 날짜를 가져온다. (YYYYMMDD)
     *
     * @return
     */
    private static String getCurrentDate() {
  Calendar cal = Calendar.getInstance();
  
  StringBuffer today = new StringBuffer();
  today.append(String.format(“%04d”, cal.get(cal.YEAR)));
  today.append(String.format(“%02d”, cal.get(cal.MONTH) + 1));
  today.append(String.format(“%02d”, cal.get(cal.DAY_OF_MONTH)));
  return today.toString();
 }
   
   
    /**
     * 현재 날짜와 시간을 슬래시 포함한 문자열로 가져온다. (0000/00/00 00:00:00)
     *
     * @return
     */
    private static String getCurrentDateTimeWithSlash() {
  Calendar cal = Calendar.getInstance();
  
  StringBuffer today = new StringBuffer();
  today.append(String.format(“%04d”, cal.get(cal.YEAR)));
  today.append(“/”);
  today.append(String.format(“%02d”, cal.get(cal.MONTH) + 1));
  today.append(“/”);
  today.append(String.format(“%02d”, cal.get(cal.DAY_OF_MONTH)));
  today.append(” “);
  today.append(String.format(“%02d”, cal.get(cal.HOUR_OF_DAY)));
  today.append(“:”);
  today.append(String.format(“%02d”, cal.get(cal.MINUTE)));
  today.append(“:”);
  today.append(String.format(“%02d”, cal.get(cal.SECOND)));
  return today.toString();
 }
}

[JAVA] SimpleProperties 클래스

[JAVA] SimpleProperties 클래스

자바에서 정석적인 프로퍼티 파일 읽기​는 아래 포스트를 참고하면 된다.

https://blog.naver.com/bb_/221666064467

위 링크는 정석적인 프로퍼티 파일 읽기이고, 본 포스트는 프로퍼티 파일 읽기를 직접 구현해본 것이다.

<SimpleProperties 클래스>

properties 파일 읽기용 자바 클래스.

주요 기능은 properties 파일을 읽어서 HashMap으로 만드는 것이다.

클래스 하나만 갖다 놓으면 properties를 읽을 수 있게 간단한 형태로 만들어보았다.

단, SimpleLogger.printLog 라는 부분은 오류가 날 것이다.

System.out.println 코드로 대체해서 사용하거나, 다음 글에서 설명할 SimpleLogger 클래스를 갖다쓰면 된다.

cf) [JAVA] SimpleLogger 클래스 : https://blog.naver.com/bb_/221656929478

———-

package com.bb.simpleset;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

public class SimpleProperties {
 
 
 /**
  * 프로퍼티 파일 읽어서 해시맵으로 리턴한다.
  *
  * @param filePath
  * @return
  * @throws IOException
  * @throws Exception
  */
 public static HashMap<String, String> readPropertiesFile(String filePath) throws IOException, Exception {
  
  if (filePath == null || filePath.length() == 0) {
   SimpleLogger.printLog(“properties filePath is null or empty.”);
   return null;
  }
  
  File file = new File(filePath);
  if (!file.exists()) {
   SimpleLogger.printLog(“properties file does not exists. [” + file.getAbsolutePath() + “]”);
   return null;
  }
  
  HashMap<String, String> resultMap = new HashMap<String, String>();
  
  try {
   ArrayList<String> propFileContent = readFile(file);
   if (propFileContent == null || propFileContent.size() == 0) {
    SimpleLogger.printLog(“properties file content is empty. [” + file.getAbsolutePath() + “]”);
   }
   
   String line = null;
   int propFileLineCount = propFileContent.size();
   for (int i=0; i<propFileLineCount; i++) {
    line = propFileContent.get(i);
    if (line == null || line.trim().length() == 0) {
     continue;
    } else {
     line = line.trim();
    }
    
    // #으로 시작하면 주석이므로 무시
    if (line.startsWith(“#”)) {
     continue;
    }
    
    int equalIndex = line.indexOf(“=”);
    if (equalIndex < 0) {
     continue;
    }
    
    String leftKey = line.substring(0, equalIndex);
    if (leftKey.trim().length() == 0) {
     continue;
    } else {
     leftKey = leftKey.trim();
    }
    
    String rightVlaue = line.substring(equalIndex + 1);
    if (rightVlaue.trim().length() == 0) {
     continue;
    } else {
     rightVlaue = rightVlaue.trim();
    }
    
    resultMap.put(leftKey, rightVlaue);
   }
   
  } catch (IOException e) {
   throw e;
   
  } catch (Exception e) {
   throw e;
  }
  
  return resultMap;
 }
 
 
 /**
  * 파일을 읽는다.
  *
  * @param file
  * @return
  * @throws IOException
  * @throws Exception
  */
 private synchronized static ArrayList<String> readFile(File file) throws IOException, Exception {
        if (file == null || !file.exists()) {
            return null;
        }

        ArrayList<String> resultList = null;

        FileInputStream fileInputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;

        try {
            fileInputStream = new FileInputStream(file);
            inputStreamReader = new InputStreamReader(fileInputStream, “UTF-8”);
            bufferedReader = new BufferedReader(inputStreamReader);

            String oneLine = null;
            while ((oneLine = bufferedReader.readLine()) != null) {
                if (resultList == null) {
                    resultList = new ArrayList<String>();
                }

                resultList.add(oneLine);
            }

        } catch (IOException e) {
            throw e;

        } catch (Exception e) {
            throw e;

        } finally {
            close(bufferedReader);
            close(inputStreamReader);
            close(fileInputStream);
        }

        return resultList;
    }
 

    private static void close(FileInputStream fileInputStream) {
        try {
            if (fileInputStream != null) {
                fileInputStream.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            fileInputStream = null;
        }
    }

   
    private static void close(InputStreamReader inputStreamReader) {
        try {
            if (inputStreamReader != null) {
                inputStreamReader.close();
            }
        } catch (Exception e) {
            // 무시

        } finally {
            inputStreamReader = null;
        }
    }
   
   
    private static void close(BufferedReader bufferedReader) {

        try {
            if (bufferedReader != null) {
                bufferedReader.close();
            }
        } catch (Exception e) {
            // 무시
         
        } finally {
            bufferedReader = null;
        }
    }
}

[iOS] iOS 앱 관련 인증서 정리 첫번째

[iOS] iOS 앱 관련 인증서 정리 첫번째

iOS 개발을 접하게 되면서 너무나 당황스럽고 어려운 것이 인증서였다.

인증서를 발급받고 등록하고 사용하는 과정도 어려웠지만,

애초에 인증서 종류가 너무 많다보니 인증서 간의 차이도 모르겠고, 도대체 왜 이렇게 많은 인증서가 필요한 것인지 짜증이 났다.

아무튼 간략하게 iOS 앱 관련 인증서를 정리해본다.

1. CSR

CSR은 Cert Signing Request 의 약자, 인증서 서명요청이라는 뜻이다.

CSR은 간단하게 말하면 인증서 발급을 위한 신청서다.

인증서라고 보기는 어렵고, 인증서 발급을 위해 필요한 것이다.

이 CSR 이라는게 상당히 헷갈리는 요소다.

왜 그래야하는지는 모르겠지만, 인증서 발급을 할 때 [CSR 발급 신청] -> [CSR 발급됨] -> [CSR로 인증서 발급 신청] -> [인증서 발급됨] 과정을 거쳐야 한다.

어째서 이렇게 복잡한 과정을 만들었는지 모르겠다.

고수 분들은 이해하겠지만 애플의 큰 뜻이 있을 것이다. 난 아직 큰 뜻 그런거 모르고 짜증난다.

2. 배포 인증서

앱을 배포하기 위한 인증서이다.

가장 중요한 인증서다.

배포 인증서가 만료되면 앱을 더 이상 사용할 수 없다고 “카더라”. 경험해본 적은 없다.

=> 2020/06/02 내용 추가. 배포 인증서가 만료되면 앱을 더 이상 사용할 수 없다. 앱 아이콘을 클릭해도 구동되지 않는다. 사용자 친화적인(user-friendly) 메시지는 나오지 않았고 그냥 잠깐 검은 화면이 나왔다가 종료된다.

3. 프로비저닝 프로파일 인증서

개발하기 위해 필수적인 인증서.

이름부터 어렵게 느껴진다.

이 인증서가 있어야 앱을 테스트할 수 있고, 앱스토어에 앱을 등록할 수 있고, 앱을 빌드할 수 있다.

앱스토어에 앱을 올리지 않아도 되는 앱이라도, 아니 심지어 앱을 테스트하기 위해서도 필요한 인증서다.

도대체 로컬 환경에서 테스트 한 번만 해보겠다는데 왜 인증서가 필요한지 모르겠다.

애플의 큰 뜻이 있을 것이다.

4. Push 인증서

앱에서 Push 메시지를 받기 위한 인증서다.

Push [푸시]란, 아이폰 기기에 토스트 메시지를 띄워주는 기능이다.

Push 인증서가 만료되면 더 이상 Push를 받지 못한다고 “카더라”. 물론 경험해본 적은 없다.

그리고 Push만 받지 못하게 되는 것이지, 배포 인증서가 만료되지 않는 한 일반적인 앱 구동에는 지장이 없다는데 이것도 카더라라서 잘 모르겠다.

5. MDM Push 인증서

MDM 환경의 앱에서 Push 메시지를 받기 위한 인증서다.

MDM 환경이란 여기서 자세히 설명하긴 어렵고 그냥 모바일 기기가 통제되는 환경이다.

MDM 환경에서는 앱을 마음대로 설치할 수도 없고, 허가되지 않은 앱을 사용할 수도 없다.

MDM 환경에서 Push를 받으려면 기본 Push 인증서도 필요하고, MDM Push 인증서도 필요하다.

정말 놀라운 애플의 정책이 아닐 수 없다.

* 아직 궁금한 내용들

답을 아시는 분들은 댓글로 꼭 써주시길 바란다.

1) 배포 인증서가 만료되면 앱 구동이 더 이상 안되는 것인가, 아니면 이미 설치한 앱은 정상 구동 가능하고 새로 설치만 불가능해지는 것인가?

=> 2020/06/02 내용 추가. 배포 인증서가 만료되면 앱을 더 이상 사용할 수 없다. 앱 아이콘을 클릭해도 구동되지 않는다. 사용자 친화적인(user-friendly) 메시지는 나오지 않았고 그냥 잠깐 검은 화면이 나왔다가 종료된다.

2) 집에서 Xcode 및 objective-c 로 헬로 월드 프로그램을 짜서 테스트해봤을 때는 프로비저닝 프로파일 인증서가 필요없었다. 그런데 회사에 이미 존재하는 프로젝트 또는 타업체에서 전달받은 테스트 프로젝트 등은 한결같이 프로비저닝 프로파일 인증서가 필요했다. 어떤 조건 이후부터 프로비저닝 프로파일 인증서가 반드시 필요한 것인가?

=> 2021/04/08 내용 추가. 실제 디바이스에서 테스트를 할 경우 반드시 개발자 인증서가 필요하다고 한다. 하지만 에뮬레이터를 사용할 경우는 기본 인증서를 사용해도 구동된다고 한다.

3) CSR은 도대체 왜 필요한 것인가? 그냥 [인증서 발급 신청] -> [인증서 발급됨] 하면 되지 않을까?

=> 2020/06/02 내용 추가. CSR을 만들 때 (1) 기관명, (2) 이메일 주소를 넣게 되어 있는데, 이것이 인증서 정보의 기초가 된다. 그냥 1단계 인증서라고 생각하면 편한 것 같다. 또한 CSR은 (1) 기관명, (2) 이메일 주소가 동일한 조건이라면 계속 재사용할 수 있다.

4) CSR이 필요한 이유가 존재한다고 해도, 그러한 조건에서만 CSR 파일을 만들면 되는 것이지, [CSR 발급 신청] -> [CSR 발급됨] -> [CSR로 인증서 발급 신청] -> [인증서 발급됨] 이 과정이 진짜 합리적인가?

=> 2020/06/02 내용 추가. 항목 3번의 답변 참고할 것.

* 글을 마무리하며

애플 인증서에 대해 쓴 내용 중 틀린 내용도 있을 것이다.

틀린 내용은 댓글로 꼭 적어주시기 바란다.

[iOS] objective-c 전역변수 사용

[iOS] objective-c 전역변수 사용

iOS 프로젝트 objective-c 언어에서 전역변수를 사용하는 방법이다.

1. 헤더 파일(확장자가 h인 파일)을 생성한다. (ex : extern.h)

2. 헤더 파일에 extern 이라는 키워드를 붙여서 전역변수로 쓸 변수를 선언한다.

예를 들면 아래와 같다.

extern double sum;

extern double average;

extern int count;

3. 확장자가 mm 인 파일을 생성한다. 헤더 파일(확장자가 h인 파일)과 동일한 이름이어야 한다. (ex : extern.mm)

4. mm 파일에서 헤더 파일(h)을 임포트하고 변수를 초기화한다.
예를 들면 아래와 같다.

#import “extern.h”

int count = 0;

double sum = 0.0;

double average = 0.0;

5. 이제 전역변수 사용을 원하는 파일에 헤더 파일을 임포트하고, 사용하면 된다.

참고사이트 : https://winplz.tistory.com/entry/Objective-C-%EC%A0%84%EC%97%AD-%EB%B3%80%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

[iOS] Xcode objective-c 앱 아이콘 수정 / 앱 아이콘 변경

[iOS] Xcode objective-c 앱 아이콘 수정 / 앱 아이콘  변경

Xcode 에서 아이폰 앱 아이콘을 수정 / 변경하는 방법.

1. Xcode 좌측 프로젝트 트리에서 확장자가 xcassets 인 파일을 찾아서 연다.

ex) 특정 폴더 밑의 Assets.xcassets 파일

ex) Resources 폴더 밑의 Images.xcassets 파일

2. 아이콘으로 쓸 그림 파일을 파일시스템 상 프로젝트 내 특정 폴더에 넣어놓는다.

Xcode 에서 하지 않고 Finder 에서 해도 된다.

3. Images.xcassets 의 AppIcon 을 보면 기존 앱 아이콘이 있을 것이다.

새 그림 파일을 기존 앱 아이콘 영역에 드래그해서 끌어다 놓으면 앱 아이콘을 변경할 수 있다.

참고사이트 : https://cofs.tistory.com/235

[Android] 레이아웃 버튼 표시 / 버튼 숨김 처리

[Android] 레이아웃 버튼 표시 / 버튼 숨김 처리

* 안드로이드에서 버튼 display none하는 방법.

안드로이드 레이아웃 버튼(ImageButton)을 표시 또는 숨김 처리하는 방법이다.

xml 에서는 다음과 같이 표시처리한다.

android:visibility=”visible”

xml 에서는 다음과 같이 숨김처리한다. 이 때, 보이지만 않을 뿐 공간은 여전히 그대로 차지한다.

android:visibility=”invisible”

xml 에서는 다음과 같이 숨김처리한다. 이 때, 보이지 않는 동시에 차지하는 공간도 사라진다.

android:visibility=”gone”

자바 코드에서는 다음과 같이 표시처리한다.

layout.setVisibility(View.VISIBLE);

자바 코드에서는 다음과 같이 숨김처리한다. 이 때, 보이지만 않을 뿐 공간은 여전히 그대로 차지한다.

layout.setVisibility(View.INVISIBLE);

자바 코드에서는 다음과 같이 숨김처리한다. 이 때, 보이지 않는 동시에 차지하는 공간도 사라진다.

layout.setVisibility(View.GONE);

참고사이트 : https://yonoo88.tistory.com/201

[Android] 텍스트뷰를 화면 정중앙에 표시하기 (gravity)

[Android] 텍스트뷰를 화면 정중앙에 표시하기 (gravity)

Android 에서 TextView 를 가로 가운데 정렬, 세로 가운데 정렬하는 방법이다.

gravity 어트리뷰트를 쓰면 된다.

xml에서는 아래와 같이 쓴다.

<TextView
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”
    android:gravity=”center”
    android:text=”표시할텍스트”

/>

자바 코드로는 (textView객체명).setGravity(Gravity.CENTER); 하면 된다.

참고사이트 : https://hashcode.co.kr/questions/55/textview%EC%9D%98-%EC%9C%84%EC%B9%98%EB%A5%BC-%EA%B0%80%EB%A1%9C-%EC%84%B8%EB%A1%9C-%EA%B0%80%EC%9A%B4%EB%8D%B0%EC%97%90-%EB%86%93%EA%B3%A0%EC%8B%B6%EC%9D%80%EB%8D%B0%EC%9A%94

[Android] The destination folder does not exist or is not writeable

[Android] The destination folder does not exist or is not writeable

안드로이드를 빌드/컴파일하는데 위와 같이 The destination folder does not exist or is not writeable 오류가 발생하는 경우.

 

Destination Folder 인풋박스 우측의 폴더 이미지를 클릭해서, 실제 존재하는 폴더경로를 지정해주고 다시 시도하면 된다.

P.S. 하도 검색을 많이 하다보니까 이런 당연한 것도 검색을 해서 알아냈다. (…)

쌩초보라 그렇다. 혹시 나같은 사람이 또 있을지 몰라서 올려둔다.

[Android] File google-services.json is missing.

[Android] File google-services.json is missing.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ‘:app:processDebugGoogleServices’.
> File google-services.json is missing. The Google Services Plugin cannot function without it.
   Searched Location:
  C:\AndroidWorkspaces\ProjectName\app\src\nullnull\debug\google-services.json
  C:\AndroidWorkspaces\ProjectName\app\src\debug\nullnull\google-services.json
  C:\AndroidWorkspaces\ProjectName\app\src\nullnull\google-services.json
  C:\AndroidWorkspaces\ProjectName\app\src\debug\google-services.json
  C:\AndroidWorkspaces\ProjectName\app\src\nullnullDebug\google-services.json
  C:\AndroidWorkspaces\ProjectName\app\google-services.json

위 메시지가 나오면 일단 안드로이드 푸시를 사용하는 프로젝트인지 확인한다.

해당 프로젝트가 푸시를 사용한다면 콘솔에 출력된 경로에 google-services.json 를 위치시켜야 한다.

google-services.json 는 구글에 gmail 계정으로 접속해서 FCM 페이지에서 다운로드 받을 수 있다.

만약 푸시를 사용하지 않는 프로젝트인 경우 build.gradle 파일 내의 google-services 버전이 너무 높지 않은지 확인한다.

1. 프로젝트 폴더 아래의 build.gradle 파일을 연다.

2. google-services 버전을 낮게 수정한다.

예) classpath ‘com.google.gms:google-services:4.0.1’ 을

classpath ‘com.google.gms:google-services:1.3.1’ 로 변경한다.

[Android] 안드로이드 프로젝트에 라이브러리 추가하는 방법

[Android] 안드로이드 프로젝트에 라이브러리 추가하는 방법 

1. 안드로이드 스튜디오 좌측상단 콤보박스를 [Android] 에서[Project]로 변경한다.

([Android] 일 경우 libs 폴더가 보이지 않기 때문임)

2. 파일시스템상 (윈도우 탐색기 등을 이용해서) libs 폴더 안에 특정 jar 파일을 넣는다.

libs 폴더를 새로고침하여 안드로이드 스튜디오 좌측 트리에 파일이 보이도록 한다.

3. 해당 jar 파일 위에서 마우스 우클릭, [Add As Library…] 를 클릭한다.

아이폰과 안드로이드 개발 단상

아이폰과 안드로이드 개발 단상

* 안드로이드 개발할 때 드는 생각 : 아이폰에 인증서가 있다면 안드로이드에는 그래들이 있다.

* 아이폰 개발할 때 드는 생각 : 안드로이드에 그래들이 있다면 아이폰에는 인증서가 있다.

어느 하나 쉬운게 없구나…

[iOS] 확장자 p12 파일 추출 (개인정보교환 파일)

[iOS] 확장자 p12 파일 추출 (개인정보교환 파일)

정확하지 않은 정보일 수 있으나 현재 소중한 정보이므로 적어둔다.

틀린 내용이 있을 경우 댓글로 달아주시면 감사.

인증서 파일은 해당 파일을 생성한 특정 맥의 정보를 포함하고 있으므로, 다른 맥에서 가져다 쓸 수 없다.

따라서 키체인 메뉴에서 인증서 파일로부터 p12 파일을 추출한다. 추출시 비밀번호도 부여한다.

이후 p12 파일을 전달, 다른 맥에서 p12 파일을 더블클릭하여 비밀번호를 입력하면 키체인에 인증서가 등록된다.

이것으로 개발을 진행하면 된다.

p12 파일을 다른 말로 개인정보교환 파일이라고 한다.

참고로 p12 파일 추출 방법은 다음과 같다.

 

 

1. 배포 인증서의 역삼각형 클릭 – 개인키(열쇠 모양 아이콘)가 나타나도록 함
2. 커맨드 키를 누른 채로 (1) 인증서와 (2) 개인키를 같이 선택
3. 마우스 우클릭 – [2개 항목 보내기…] 클릭

4. 저장 위치 지정, 비밀번호 부여, 키체인 비밀번호(맥 로그인 비밀번호) 입력 등 진행하면 p12 파일 추출 성공

<엑셀VBA 입문 18강> indexOf 함수의 확장

<엑셀VBA 입문 18강> indexOf 함수의 확장

아래 질문을 받아서 잠시 짬을 내 글을 써봅니다.

안녕하세요. 올려주신 엑셀VBA 입문 5강을 잘 읽었습니다!
그런데 예시 들어주신 INDEXOF 라든지 INCLUDE 의 경우
원하는 단어가 (1) 포함되는 경우 (2) 포함되지 않는 경우 이렇게 2가지만 구분할 수 있습니다.

저는 3가지 이상의 경우의 수로 구분하고 싶은데 가능할까요?

예시. 파주, 양주 라는 단어가 포함일 경우 경기도라는 값이 나오고
속초, 양양 이라는 단어가 포함될 경우 강원도라는 값이 나오게 할 수 있을까요?

vba 에서 기본적인 indexOf 함수는 아래처럼 짤 수 있습니다.

Function indexOf(cell, textToFind)

    ‘cell의 값을 가져옴
    Dim val
    val = Range(cell, cell).Value

    ‘cell의 값 1번째 글자부터 textToFind 값 찾기
    Dim idx
    idx = InStr(1, val, textToFind, vbTextCompare)

    ‘인덱스 값을 리턴

    ‘리턴값이 1 이상이면 포함되는 경우이고, 리턴값이 0 이면 포함되지 않는 경우
    indexOf = idx

End Function

이 기본적인 indexOf는 원하는 단어가 (1) 포함되는 경우 (2) 포함되지 않는 경우 이렇게 2가지만 구분할 수 있습니다.

리턴값이 1 이상이면 포함되는 경우이고, 리턴값이 0 이면 포함되지 않는 경우입니다.

만약 3가지 이상의 경우의 수로 구분하려면 다음과 같이 작성하면 됩니다.

Function indexOf2(cell)

    ‘cell의 값을 가져옴
    Dim val
    val = Range(cell, cell).Value

    ‘cell의 값 1번째 글자부터 값 체크
    If InStr(1, val, “파주”, vbTextCompare) > 0 Then
        indexOf2 = “경기도”
   
    ElseIf InStr(1, val, “양주”, vbTextCompare) > 0 Then
        indexOf2 = “경기도”
       
    ElseIf InStr(1, val, “속초”, vbTextCompare) > 0 Then
        indexOf2 = “강원도”
   
    ElseIf InStr(1, val, “양양”, vbTextCompare) > 0 Then
        indexOf2 = “강원도”
       
    Else
        ‘아무 것도 해당하지 않을 경우
        indexOf2 = “없음”
    End If

End Function

실제 함수의 사용 결과는 아래와 같습니다.



 

[iOS] Xcode : Command PhaseScriptExecution failed with a nonzero exit code

[iOS] [Xcode] Command PhaseScriptExecution failed with a nonzero exit code

Command PhaseScriptExecution failed with a nonzero exit code

Xcode 에서 iOS 빌드했을 때 위와 같은 오류가 발생하는 경우.

1. Xcode 의 상단메뉴 [File] – [Workspace Settings…] 클릭

2. Build System 을 Legacy Build System 으로 변경하여 프로젝트 다시 빌드해볼 것.

[iOS] Xcode : Pods-ProjectName-frameworks.sh: Permission denied

[iOS] [Xcode] Pods-ProjectName-frameworks.sh: Permission denied

line 2: /Users/username/Documents/workspace/ProjectFolder/ProjectName/Pods/Target Support Files/Pods-ProjectName/Pods-ProjectName-frameworks.sh: Permission denied

Xcode 에서 iOS 프로젝트를 빌드했을 때 위와 같은 오류가 발생하는 경우.

1. cmd 실행

2. 해당 파일이 존재하는 위치로 이동 (ex : cd /Users/username/Documents/workspace/ProjectFolder/ProjectName/Pods/Target Support Files/Pods-ProjectName)

3. 해당 파일에 권한부여

chmod +x “Pods-ProjectName-frameworks.sh”

4. 프로젝트를 다시 빌드해본다. 해당 오류는 사라져서 빌드 성공하거나, 다음 오류로 넘어갈 것이다.

[iOS] library not found for -lAFNetworking

[iOS] library not found for -lAFNetworking

Xcode에서 프로젝트를 빌드했을 때 library not found for -lAFNetworking 오류가 발생하는 경우.

1. 프로젝트를 열 때 [파일명].xcodeproj 파일을 열지 말고, [파일명].xcworkspace 파일을 열어서 다시 빌드해볼 것.

2. 만약 1번 을 수행했는데도 안된다면, 해당 프로젝트(AFNetworking)를 찾아서 [Basic] 탭 – [Architectures] 항목의 Build Active Architecture Only 를 찾아서, [Debug], [Release], [ad Hoc Distribution] 에 대해서 모두 [No]로 지정한 후 다시 빌드해볼 것.

(안되면 Yes 로 수정한 후 빌드, 다시 No 로 수정해서 빌드해볼 것)

[Eclipse] Selected SVN connector library is not available or cannot be loaded.

[Eclipse] Selected SVN connector library is not available or cannot be loaded.

SVN: ‘0x00400006: Validate Repository Location’ operation finished with error: Selected SVN connector library is not available or cannot be loaded.

If you selected native JavaHL connector, please check if binaries are available or install and select pure Java Subversion connector from the plug-in connectors update site.

If connectors already installed then you can change the selected one at: Window->Preferences->Team->SVN->SVN Connector.

Selected SVN connector library is not available or cannot be loaded.

If you selected native JavaHL connector, please check if binaries are available or install and select pure Java Subversion connector from the plug-in connectors update site.

If connectors already installed then you can change the selected one at: Window->Preferences->Team->SVN->SVN Connector.

1. 맥(Mac)의 경우 상단 메뉴의 [Eclipse] – [환경설정] 메뉴 클릭

2. 좌측 트리 메뉴의 [Team] – [SVN] – 상단 탭의 [SVN Connector] – [Get Connectors…] 버튼 클릭

3. [SVN Kit 1.8.14] 선택 후 [Finish], [Next] 등의 버튼을 눌러 계속 진행

참고사이트 : https://asource.tistory.com/12

[Android] Cleartext HTTP traffic to 000.000.000.000 not permitted

[Android] Cleartext HTTP traffic to 000.000.000.000 not permitted

Cleartext HTTP traffic to not permitted 오류가 발생할 경우

1. http 주소를 https 주소로 변경하면 해결.

2. 1번이 불가능하다면, targetSdkVersion 를 28 미만으로 사용하면 해결.

build.gradle (app) 파일의 targetSdkVersion 를 수정.

3. 1번과 2번이 불가능하다면 (http 를 사용해야만 하고, targetSdk를 28 이상으로 써야 한다면),

앱 매니페스트에 다음과 같이 기입한다.

<?xml version=”1.0″ encoding=”utf-8″?>
    <manifest … >
        <application

          …

          android:usesCleartextTraffic=”true”>
            …
        </application>
    </manifest>

도메인별 허용 등 다른 방법도 있다. 다른 방법은 아래 사이트들을 참고할 것.

참고 사이트 : https://gun0912.tistory.com/80

참고 사이트 2 : http://sunphiz.me/wp/archives/3094

[iOS] objective-c sleep

[iOS] objective-c sleep

// 2초 sleep

[NSThread sleepForTimeInterval:2.000];

[iOS] objective-c 비동기 처리 (dispatch_async)

[iOS] objective-c 비동기 처리 (dispatch_async)

아이폰 앱에서 로그인시 VPN 연동을 개발하고 있었는데, VPN 접속 시간이 몇 초 걸리는 관계로 연결 성공 여부를 곧바로 체크할 수 없는 상황이었다. 쓰레드 또는 비동기 처리가 필요했는데, 쓰레드는 다소 어려워보였고 GCD(Grand Central Dispatch) 라는 개념의 코드가 있어서 가져다 잘 썼다.

코드는 아래와 같으며, 코드 출처는 http://seorenn.blogspot.com/2012/04/ios.html 이다.

// 여기서부터 비동기 코드 시작.

// dispatch_async 함수는 내부블럭의 코드 실행에 영향을 받지 않고 바로 실행이 끝난다.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

    // 작업이 오래 걸리는 API를 백그라운드 스레드에서 실행한다.

    BOOL res = [self heavyOperatingMethod];

    dispatch_async(dispatch_get_main_queue(), ^{

        // 이 블럭은 메인스레드(UI)에서 실행된다.

        if (res) {

            [self operationSucceed];

        } else {

            [self operationFailed];

        }

    });

});

위 예제 코드를 십분 활용하여 실제로는 아래와 같이 짰다.

-(BOOL)loginVPN {

        // (중략. VPN 연결 시도하는 코드)

        // VPN 연결 체크

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

            // VPN 연결 성공했는지 비동기로 체크

            BOOL res = [self checkVPNConnection];

            

            dispatch_async(dispatch_get_main_queue(), ^{

                // 비동기 메서드 결과가 리턴되면 아래 로직 수행

                if (res) {

                    [self loginAfterVPN];

                } else {

                     [LoginUtil alert:@”VPN 연결이 원활하지 않습니다. 잠시후 다시 시도해주세요.”];

                }

            });

        });

        

        // NO를 리턴하면 다음 로직을 타지 않게 됨. 다음 로직은 비동기 메서드의 결과가 리턴되면 loginAfterVPN 에서 계속됨.

        return NO;

}

// VPN 연결 성공했는지 비동기로 체크.

// 반복문을 돌면서 2초마다 한 번씩 VPN 연결 성공했는지 체크.

-(BOOL)checkVPNConnection {

    

    for (int i=0; i<10; i++) {

        NSLog(@”CHECK VPN…”);

        

        BOOL t2 =  [self vpnStatusCheck];

        if (t2) {

            return YES;

        }

        

        // 2초 sleep

        [NSThread sleepForTimeInterval:2.000];

    }

    

    return NO;

}

[iOS] int to NSString

[iOS] int to NSString

int i = 10;

NSString *str = [NSString stringWithFormat:@”%d”, i];

[iOS] NSURL to NSString

[iOS] NSURL to NSString

NSString *myString = [myURL absoluteString];

또는

NSString *myString = [myURL path];

[iOS] Could not launch “ProjectName”

[iOS] Could not launch “ProjectName”

Could not launch “ProjectName”

Project has denied the launch request. Internal launch error: process launch failed: failed to get the task for process 803

맥에 아이폰을 연결하여 Xcode 에서 프로젝트 디버그를 시도했을 때, 위와 같은 메시지가 뜨며 앱이 실행되지 않는 경우.

완벽한 해결법은 알 수 없지만 디버그 모드를 끄고 실행해볼 수 있다.

라인 바이 라인으로 쫓아가며 디버그를 해볼 수는 없지만, 빌드할 때마다 ipa 파일을 옮겨서 설치하고 테스트하는 번거로움은 피할 수 있다.

Xcode 상단 메뉴 [Product] – [Scheme] – [Edit Scheme…] – [Info]  탭 상단의 [Debug executable] 체크박스를 체크 해제하고 다시 디버그 시도해본다.

[iOS] [KeychainItemWrapper getUUID]: unrecognized selector sent to instance 0x2829369c0

[iOS] [KeychainItemWrapper getUUID]: unrecognized selector sent to instance 0x2829369c0

도대체가 이유를 알 수 없지만 Xcode 콘솔에 아래와 같은 오류가 발생하는 경우.

*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[KeychainItemWrapper getUUID]: unrecognized selector sent to instance 0x2829369c0’

*** First throw call stack:

(0x18b8ed27c 0x18aac79f8 0x18b809ab8 0x18b8f2ac4 0x18b8f475c 0x104c08168 0x104bef108 0x104beed0c 0x104beec70 0x1b80b1040 0x1b7b5a1c8 0x1b7b5a4e8 0x1b7b59554 0x1b80e8304 0x1b80e952c 0x1b80c959c 0x1b818f714 0x1b8191e40 0x1b818b070 0x18b87f018 0x18b87ef98 0x18b87e880 0x18b8797bc 0x18b8790b0 0x18da7979c 0x1b80af978 0x104be9c10 0x18b33e8e0)

libc++abi.dylib: terminating with uncaught exception of type NSException

문자 그대로 해석해보면 KeychainItemWrapper 파일의 getUUID 메서드가 없다는 오류이다.

필자의 상황은 외부 업체의 라이브러리를 사용했는데 위 오류가 발생하였다. 해당 라이브러리는 C로 구현된 것으로, 뜯어서 내부를 보고 싶었지만 그럴 수가 없어서 어쩔 수 없이 getUUID 메서드를 구현해주었다.

아마도 KeychainItemWrapper.h 파일과 KeychainItemWrapper.m 파일이 프로젝트 내에 구현되어 있을 것이다.

해당 파일을 찾아서 수정한다.

1. KeychainItemWrapper.h 파일에 내용 추가

– (NSString*) getUUID;

2. KeychainItemWrapper.m 파일에 내용 추가

// 처음에 UUID를 KeyChain에서 불러오는데 nil이라면 UUID를 생성해서 KeyChain에 저장한다.
// 저장 후에 다시 함수를 호출 하면 저장된 값을 리턴한다.
– (NSString*) getUUID {
    
    // initialize keychaing item for saving UUID.
    KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@”UUID” accessGroup:nil];
    
    NSString *uuid = [wrapper objectForKey:(__bridge id)(kSecAttrAccount)];
    
    if (uuid == nil || uuid.length == 0) {
        
        // if there is not UUID in keychain, make UUID and save it.
        CFUUIDRef uuidRef = CFUUIDCreate(NULL);
        CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
        CFRelease(uuidRef);
        uuid = [NSString stringWithString:(__bridge NSString *) uuidStringRef];
        CFRelease(uuidStringRef);
        
        // save UUID in keychain
        [wrapper setObject:uuid forKey:(__bridge id)(kSecAttrAccount)];
    }
    
    return uuid;
}

메서드의 내용의 출처는 다음 주소에서 가져왔다. 이 자리를 빌어 감사드린다.