2020 스마트민방위교육 평가 답안

2020 스마트민방위교육 평가 답안

1. 민방위대는 만20세가 되는 해의 1월 1일부터 만40세가 되는 해의 12월 31일까지의 대한민국 국민인 남성으로 조직된다. => 맞다

2. 화학가스 사고시 가스나 오염물질이 들어오지 않도록 창문과 문틈을 밀폐해야 한다. => 맞다

3. 낯선 장소의 수상한 물건과 발신명 등이 불분명한 소포는 몰래 뜯어보아야 한다. => 틀리다

4. 핵 공격시 민방공 대피소나 가까운 지하시설로 대피해야 한다. => 맞다

5. 심폐소생술은 119가 도착할 때까지 계속해야 한다. => 맞다

6. 화재시 ‘불이야’를 외치고 비상벨을 눌러 주변 사람에게 화재 사실을 알려야 한다. => 맞다

7. 소화기는 바람을 등지고 빗자루로 쓸듯이 뿌려야 한다. => 맞다

8. 화재시 불과 연기의 확산 방지를 위해 방화문을 닫아 놓아야 한다. => 맞다

9. 소화기 사용법은 ‘①안전핀을 뽑고 ②호스를 불이 난 곳으로 향한 다음 ③손잡이를 움켜쥔다’ 이다. => 맞다

10. 119 신고시 불이 난 주소와 상황을 간단명료하게 설명해야 한다. => 맞다

11. 화학·방사능 테러 발생시 현장을 벗어났다면 비눗물로 씻고 옷은 비닐봉지에 넣어 폐기해야 한다. => 맞다

12. 공연장, 극장, 행사장, 경기장, 백화점, 마트 등 사람이 많이 모이는 곳에서는 항상 안전에 주의해야 한다. => 맞다

13. 복합건물 출입시 비상상황을 대비해 미리 탈출구를 확인해야 한다. => 맞다

14. 지진해일 발생시 튼튼한 건물 3층 이상으로 대피하는 것도 한 방법이다. => 맞다

15. 대한민국에서 지진이 일어난 적이 없다. => 틀리다

16. 지진 발생시 블록담, 대문, 기둥, 자동판매기 등에 기대면 안전하다. => 틀리다

17. 태풍 예보시 지붕, 간판, 창문, 출입문 등은 단단히 고정해야 한다. => 맞다

18. 태풍이 지나가면 물은 오염될 수 있으므로 반드시 끓여 마신다. => 맞다

19. 태풍 예보시 공사장, 축대, 옹벽, 산비탈 경사면 등을 미리 점검하는 것이 좋다. => 맞다

20. 태풍이 지나간 후 침수된 집안은 가스가 누출될 수 있으므로 환기 후 출입해야 한다. => 맞다

Microsoft Office Word 2007 pdf로 저장하는 방법

Microsoft Office Word 2007 pdf로 저장하는 방법

Word 2007 을 pdf 확장자로 저장하려고 할 때 위 이미지처럼 pdf 확장자를 선택할 수 없는 경우. 또는 vb/vba/vbs 에서 pdf파일로 SaveAs 명령어를 수행했을 때 오류가 발생하는 경우. (cf : objDoc.SaveAs sPdfFile, wdFormatPDF)

기존의 해결책은, Word 2007 또는 Excel 2007 프로그램을 실행하고 좌측상단 오피스 아이콘 클릭 – [다른 이름으로 저장(A)] – [다른 파일 형식에 대한 추가 기능 찾기(F)] 에서 [2007 Miscrosoft Office 프로그램의 Miscrosoft PDF 및 XPS로 게시 추가 기능]을 설치하는 것이었다고 한다(관련링크 : https://mpain.tistory.com/25).

하지만 현재(2020년 9월)는 위 방법을 사용할 수가 없는게, 오피스 365로 업그레이드하라는 메시지가 나온다. 하긴 오피스 2007이 나온지도 이제 13년이 지났다.

다른 해결책은 첨부한 설치파일 SaveAsPDFandXPS.exe 를 실행하는 것이다. 13년 전 이 exe를 업로드해준 블로거 분께 감사한 마음 뿐이다. (관련링크 : https://inchori.tistory.com/entry/Office-2007-PDF-%EB%98%90%EB%8A%94-XPS-%ED%8C%8C%EC%9D%BC%EB%A1%9C-%EC%A0%80%EC%9E%A5-Add-in?category=37662)

SaveAsPDFandXPS.exe 설치파일의 기능은 다음과 같다.

2007 Microsoft Office 추가 기능: Microsoft PDF 또는 XPS로 저장 (SaveAsPDFandXPS.exe)
간략한 설명 : 이 다운로드를 설치하면 Microsoft Office 2007의 8개 프로그램에서 파일을 PDF 및 XPS 형식으로 내보내거나 저장할 수 있습니다. 또한 이러한 프로그램의 하위 집합에서 PDF 및 XPS 형식으로 된 전자 메일 첨부 파일을 보낼 수도 있습니다

Structured Storage Viewer (SSView.exe)

Structured Storage Viewer (SSView.exe)

문서 구조(Document Structure)를 들여다볼 수 있는 일종의 Doc Viewer.

라이센스는 개인용이거나 교육 목적이면 무료(Free to use for private, educational and non-commercial purposes).

출처: https://www.mitec.cz/ssv.html

[Apple] 애플 이중인증 전화번호 추가

[Apple] 애플 이중인증 전화번호 추가

1. appleid.apple.com 에 접속한다.

2. Apple ID와 비밀번호를 입력하고 이중인증 등을 거쳐 로그인한다.

3. 로그인 성공하면 자신의 Apple ID 정보가 보인다.

해당 페이지에서 [보안] 항목을 찾고 [수정] 버튼을 클릭한다.

4. [신뢰하는 전화번호 추가…] 버튼을 통해 전화번호를 추가하면 된다.

참고사이트 : https://dejavuqa.tistory.com/384

[Mac] 맥에서 스크린샷 캡처 단축키

[Mac] 맥에서 스크린샷 캡처 단축키

전체 스크린샷 단축키은 Shift + Command + 3 이고,

부분(화면 일부) 스크린샷 단축키는 Shift + Command + 4 다.

화면 일부를 캡처하는 방법

1. shift, command 및 4 키를 동시에 길게 누릅니다.
2. 십자형 커서를 드래그하여 캡처할 화면 영역을 선택합니다.
3. 스크린샷을 찍으려면 마우스 또는 트랙패드 버튼을 놓습니다.
4. 화면 모서리에 축소판이 표시되면 축소판을 클릭하여 스크린샷을 편집합니다.

참고사이트 : https://support.apple.com/ko-kr/HT201361

[JAVA] Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/geronimo/mail/util/Base64Encoder

[JAVA] Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/geronimo/mail/util/Base64Encoder

Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/geronimo/mail/util/Base64Encoder
Caused by: java.lang.ClassNotFoundException: org.apache.geronimo.mail.util.Base64Encoder
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    … 4 more

위 오류가 발생한 경우, 프로젝트 클래스 패스에 geronimo-javamail_1.4_mail-1.5.jar 라이브러리를 추가하면 된다.

다운로드 링크

https://mvnrepository.com/artifact/org.apache.geronimo.javamail/geronimo-javamail_1.4_mail/1.5

Maven

<!– https://mvnrepository.com/artifact/org.apache.geronimo.javamail/geronimo-javamail_1.4_mail –>

<dependency>

    <groupId>org.apache.geronimo.javamail</groupId>

    <artifactId>geronimo-javamail_1.4_mail</artifactId>

    <version>1.5</version>

</dependency>

[XML] “[xX][mM][lL]”과 일치하는 처리 명령 대상은 허용되지 않습니다.

[XML] “[xX][mM][lL]”과 일치하는 처리 명령 대상은 허용되지 않습니다.

XML 파싱 시 “[xX][mM][lL]”과 일치하는 처리 명령 대상은 허용되지 않습니다. 오류가 발생하는 경우.

xml 선언문(<?xml) 앞에 공백이나 주석같은 다른 문구가 있으면 안된다.
파일 맨 첫줄의 첫 시작이 선언문이여야 한다.

또는 xml 선언문(<?xml)이 2번 이상 포함됐을 때 발생하는 오류 메시지임.

출처 : https://sarc.io/index.php/forum/question-and-answer/996-xml-fatal-error-2-6-xx-mm-ll

[javascript] UTF-8 을 고려한 js base64 encode / js base64 decode

[javascript] UTF-8 을 고려한 js base64 encode / js base64 decode

var Base64={
    _keyStr:“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=”,
    encode:function(e) {
        var t=“”;
        var n,r,i,s,o,u,a;
        var f=0;
        e=Base64._utf8_encode(e);
        
        while (f<e.length) {
            n=e.charCodeAt(f++);
            r=e.charCodeAt(f++);
            i=e.charCodeAt(f++);
            s=n>>2;
            o=(n&3)<<4|r>>4;
            u=(r&15)<<2|i>>6;
            a=i&63;
            
            if (isNaN(r)) {
                u=a=64;
            } else if(isNaN(i)) {
                a=64;
            }
            
            t=t+this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a);
        }
            
        return t;
    },
    decode:function(e) {
        var t=“”;
        var n,r,i;
        var s,o,u,a;
        var f=0;
        e=e.replace(/[^A-Za-z0-9+/=]/g,“”);
        
        while (f<e.length) {
            s=this._keyStr.indexOf(e.charAt(f++));
            o=this._keyStr.indexOf(e.charAt(f++));
            u=this._keyStr.indexOf(e.charAt(f++));
            a=this._keyStr.indexOf(e.charAt(f++));
            n=s<<2|o>>4;
            r=(o&15)<<4|u>>2;
            i=(u&3)<<6|a;
            t=t+String.fromCharCode(n);
            
            if (u!=64) {
                t=t+String.fromCharCode(r);
            }
        
            if (a!=64) {
                t=t+String.fromCharCode(i);
            }
        }
        
        t=Base64._utf8_decode(t);
        
        return t;
    },
    _utf8_encode:function(e) {
        e=e.replace(/rn/g,“n”);
        var t=“”;
        
        for (var n=0;n<e.length;n++) {
            var r=e.charCodeAt(n);
            
            if (r<128) {
                t+=String.fromCharCode(r);
            } else if(r>127&&r<2048) {
                t+=String.fromCharCode(r>>6|192);
                t+=String.fromCharCode(r&63|128);
            } else {
                t+=String.fromCharCode(r>>12|224);
                t+=String.fromCharCode(r>>6&63|128);
                t+=String.fromCharCode(r&63|128);
            }
        }
        
        return t;
    },
    _utf8_decode:function(e) {
        var t=“”;
        var n=0;
        var r=c1=c2=0;
    
        while (n<e.length) {
            r=e.charCodeAt(n);
            if (r<128) {
                t+=String.fromCharCode(r);
                n++;
            } else if (r>191&&r<224) {
                c2=e.charCodeAt(n+1);
                t+=String.fromCharCode((r&31)<<6|c2&63);
                n+=2;
            } else {
                c2=e.charCodeAt(n+1);
                c3=e.charCodeAt(n+2);
                t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);
                n+=3;
            }
        }        
        return t;
    }    
}

// 인코딩 테스트
var string = ‘Hello World!’;
var encodedString = Base64.encode(string);
console.log(encodedString); // Outputs: “SGVsbG8gV29ybGQh”

// 디코딩 테스트
var decodedString = Base64.decode(encodedString);
console.log(decodedString); // Outputs: “Hello World!” 

참고사이트 : https://devlin.tistory.com/1

[JAVA] Gradle 5.2.1 requires Java 8 or later to run. You are currently using Java 7.

[JAVA] Gradle 5.2.1 requires Java 8 or later to run. You are currently using Java 7.

터미널에서 gradlew 명령 실행시 아래와 같이 오류나는 경우.

FAILURE: Build failed with an exception.

* What went wrong:

Gradle 5.2.1 requires Java 8 or later to run. You are currently using Java 7. 

환경변수의 JAVA_HOME 을 JDK 1.8 설치경로로 변경해줘야 한다.

JAVA_HOME 확인은 다음 명령어로 한다.

윈도우

echo %JAVA_HOME%

리눅스

echo $JAVA_HOME

자바 환경변수 변경은 제어판 – 모든 제어판 항목 – 시스템 – 좌측 메뉴의 [고급 시스템 설정] – 시스템 속성 윈도우 상단의 [고급] 탭 – 하단의 [환경 변수(N)…] 버튼 클릭 – 목록에서 JAVA_HOME 항목을 찾아 [편집(E)…] 버튼 클릭하고 올바른 경로로 수정하면 된다.

[JAVA] Execution failed for task ‘:test’. No tests found for given includes:

[JAVA] Execution failed for task ‘:test’. No tests found for given includes:

아래와 같이 No tests found for given includes: 오류가 발생하는 경우

FAILURE: Build failed with an exception.

* What went wrong:

Execution failed for task ‘:test’.

> No tests found for given includes: [com.thkmon.ddoc.web.HelloControllerTest.testReturnHello](filter.includeTestsMatching)

오류가 발생한 java 파일(여기서는 HelloControllerTest.java 파일) 안에 테스트 클래스가 없기 때문이다.

내 경우는 테스트 메서드 위에 어노테이션(@Test)을 빼먹었기 때문에 발생한 오류였다.


참고사이트 : https://www.inflearn.com/questions/15495

[JAVA] POST 로 json 데이터 전송하기

[JAVA] POST 로 json 데이터 전송하기

json 데이터가 아닌 일반 파라미터로 POST 호출하는 방식은 다음 포스트 참고 : https://blog.naver.com/bb_/222539952177

 

    public String sendJsonPost(String surl, String encode, String jsonString) throws IOException, Exception {
        if (encode == null || encode.length() == 0) {
            encode = “UTF-8”;
        }

        String result = “”;

        HttpURLConnection ucon = null;
        InputStreamReader isr = null;
        InputStream is = null;
        BufferedReader br = null;

        try {
            URL url = new URL(surl);
            ucon = (HttpURLConnection) url.openConnection();

            ucon.setDoOutput(true);
            ucon.setRequestProperty(“Content-Type”“application/json”);
            ucon.setConnectTimeout(3 * 1000);
            ucon.setReadTimeout(3 * 1000);
            ucon.setUseCaches(false);

            if (jsonString != null && jsonString.length() > 0) {
                OutputStream os = null;
                OutputStreamWriter writer = null;
                try {
                    os = ucon.getOutputStream();
                    writer = new OutputStreamWriter(os);
                    writer.write(jsonString);
                    writer.close();
                    os.close();

                } catch (IOException e) {
                    throw e;

                } catch (Exception e) {
                    throw e;

                } finally {
                    close(writer);
                    close(os);
                }
            }

            ucon.connect();

            is = ucon.getInputStream();
            isr = new InputStreamReader(is, encode);
            br = new BufferedReader(isr);

            StringBuffer resultBuff = new StringBuffer();

            String line = “”;
            while ((line = br.readLine()) != null) {
                resultBuff.append(line);
            }

            result = resultBuff.toString();

        } catch (IOException e) {
            throw e;

        } catch (Exception e) {
            throw e;

        } finally {
            close(br);
            close(isr);
            close(is);
            disconnect(ucon);
        }

        return result;
    }

    public void close(OutputStreamWriter writer) {
        try {
            if (writer != null) {
                writer.close();
            }
        } catch (IOException e) {
        } catch (Exception e) {
        } finally {
            writer = null;
        }
    }

    public void close(OutputStream os) {
        try {
            if (os != null) {
                os.close();
            }
        } catch (IOException e) {
        } catch (Exception e) {
        } finally {
            os = null;
        }
    }

    public void close(BufferedReader br) {
        try {
            if (br != null) {
                br.close();
            }
        } catch (IOException e) {
        } catch (Exception e) {
        } finally {
            br = null;
        }
    }

    public void close(InputStreamReader isr) {
        try {
            if (isr != null) {
                isr.close();
            }
        } catch (IOException e) {
        } catch (Exception e) {
        } finally {
            isr = null;
        }
    }

    public void close(InputStream is) {
        try {
            if (is != null) {
                is.close();
            }
        } catch (IOException e) {
        } catch (Exception e) {
        } finally {
            is = null;
        }
    }

    public void disconnect(HttpURLConnection ucon) {
        try {
            if (ucon != null) {
                ucon.disconnect();
            }
        } catch (NullPointerException e) {
        } catch (Exception e) {
        } finally {
            ucon = null;
        }
    }

 

[Android] 안드로이드 브로드캐스트(Broadcast) 송신/수신 방법

[Android] 안드로이드 브로드캐스트(Broadcast) 송신/수신 방법

■ 안드로이드 브로드캐스트 송신(보내기)

브로드캐스트를 보내고 싶은 앱에 아래 코드를 작성한다.

1. 원하는 위치에 브로드캐스트를 송신하는(보내는) 코드를 작성한다.

    // new Intent(“전송하고 싶은 내용”)을 쓰면 된다. 실제 패키지 구조와 상관없이 전송하고 싶은 내용을 적으면 된다.

    Intent intent = new Intent(com.bb.test.TEST_MESSEGE);

    // intent.setPackage(“실제 패키지 경로”)를 써야 한다. 여기에는 수신받을 앱의 실제 패키지 경로를 적어야 한다.
    intent.setPackage(“com.bb.testapp”);

   
    getApplicationContext().sendBroadcast(intent);

■ 안드로이드 브로드캐스트 수신(받기)

브로드캐스트를 받고 싶은 앱에 아래 코드를 작성한다.

1. BroadcastReceiver 타입의 변수를 선언한다.

    public BroadcastReceiver mBroadTestReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(com.bb.test.TEST_MESSEGE)) {
                // 브로드캐스트 수신받았을 때 실행할 내용
            }
        }
    };

2. onResume() 메서드 안에 registerReceiver 코드 추가하고, onPause() 메서드 안에 unregisterReceiver 코드 추가한다.

registerReceiver 를 onCreate() 메서드 안에 추가해도 되는데, 이 경우 unregisterReceiver 코드는 onDestroy() 메서드 안에 추가하면 된다.

    @Override
    protected void onResume() {
        super.onResume();

        
        IntentFilter intentFilter = new IntentFilter(com.bb.test.TEST_MESSEGE);
        activity.registerReceiver(mBroadTestReceiver, intentFilter);
    }

    @Override
    protected void onPause() {
        super.onPause();

       

        if (mBroadTestReceiver != null) {
            unregisterReceiver(mBroadTestReceiver);

        }
    }

[Android] 안드로이드 기기 바탕화면의 앱 숨김처리

[Android] 안드로이드 기기 바탕화면의 앱 숨김처리

AndroidManifest.xml 파일에서 intent-filter 태그 내용을 아래처럼 수정한다.

<!– 바탕화면에 앱 아이콘 표시  –>

<!–

<intent-filter>
    <action android:name=”android.intent.action.MAIN” />
    <category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>

–>

<!– 바탕화면에 앱 아이콘 숨김  –>

<intent-filter>
    <action android:name=“android.intent.action.MAIN” />
    <category android:name=“android.intent.category.DEFAULT” />
</intent-filter>

앱을 숨기는 이유는 또다른 앱을 통해서 실행하기 위한 목적이다.

숨겨진 앱을 실행하는 코드는 다음과 같이 작성하면 된다.

    Intent intent = new Intent();

    // intent.setClassName(패키지명, 클래스명);
    intent.setClassName(“com.bb.testapp”, com.bb.testapp.MainActivity”);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

   

    startActivity(intent);

참고로 앱을 숨기게 되면 안드로이드 스튜디오에서 앱 실행 시 “Error running ‘app’: Default Activity not found” 오류가 발생한다.

안드로이드 스튜디오 상단메뉴 [Run] – [Edit Configurations…] 에 들어간다.

 

Launch Optioins 항목의 Launch 콤보박스 값을 [Nothing] 으로 변경하고 [OK] 버튼을 클릭한다.


 

[Android] 안드로이드 설치되어 있는 다른 앱(외부 앱) 실행

[Android] 안드로이드 설치되어 있는 다른 앱(외부 앱) 실행

다음은 앱이 설치되어 있는지 여부를 패키지 명으로 검사하는 메서드다.

    public boolean checkPackageExisting(String packageName) {
        if (packageName == null || packageName.length() == 0) {
            return false;
        }

        boolean bExist = false;

        PackageManager pkgMgr = getPackageManager();
       
        Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
        List<ResolveInfo> mApps = pkgMgr.queryIntentActivities(mainIntent, 0);

        if (mApps != null && mApps.size() > 0) {
            for (int i = 0; i < mApps.size(); i++) {
                try {
                    if (mApps.get(i).activityInfo.packageName.startsWith(packageName)) {
                        bExist = true;
                        break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    bExist = false;
                }
            }
        }

        return bExist;
    }

설치되어 있는 다른 앱(외부 앱)을 실행하는 코드는 다음과 같다.

    String packageName = “com.bb.testapp”;
    if (checkPackageExisting(packageName)) {
        Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

       
        startActivity(intent);
    }

이 때, 외부 앱을 실행하기 위한 Intent 를 사용하는 방법이 여러가지 있다.

(1) LAUNCHER 카테고리에 해당하는 액션을 실행하려면 다음과 같이 쓰면 된다.

String packageName = “com.bb.testapp”;

Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);

대상이 되는 어플리케이션의 AndroidManifest.xml 에는 다음과 같이 정의되어 있어야 한다.

<intent-filter>
    <action android:name=”android.intent.action.MAIN” />
    <category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>

(2) 특정 패키지의 특정 액티비티를 실행하려면 다음과 같이 쓰면 된다.

Intent intent = new Intent();
intent.setClassName(“com.bb.testapp”, “com.bb.testapp.MainActivity”);

대상이 되는 어플리케이션의 com.bb.testapp 패키지 하위에 MainActivity.java 파일이 있어야 한다.

(3) DEFAULT 카테고리에 해당하는 액션을 실행하려면 다음과 같이 쓰면 된다.

Intent intent = new Intent(“com.bb.testapp.action.MAIN”);

대상이 되는 어플리케이션의 AndroidManifest.xml 에는 다음과 같이 정의되어 있어야 한다.

<intent-filter>
    <action android:name=”com.bb.testapp.action.MAIN” />
    <category android:name=”android.intent.category.DEFAULT” />
</intent-filter>

만약 외부 앱을 실행하면서 동시에 데이터를 전달하고 싶다면 intent.putExtra 메서드를 사용한다.

    String packageName = “com.bb.testapp”;
    if (checkPackageExisting(packageName)) {
        Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
       

        // 외부 앱에 데이터를 전달
        intent.putExtra(“key1”, “value1”);
        intent.putExtra(“key2”, “value2”);
        intent.putExtra(“key3”, “value3”);
       
        startActivity(intent);
    }

한편, 호출당하는 외부 앱 입장에서 값을 전달받기 위해서는 intent.getStringExtra 메서드를 사용하면 된다.

호출당하는 앱의 메인 액티비티 onCreate 메서드 내용을 아래와 같이 코딩한다.

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // 중략

        Intent intent = getIntent();
        String extraKey1 = intent.getStringExtra(“key1”);

        System.out.println(“extraKey1 : “ + extraKey1);
        Toast.makeText(this, “extraKey1 : “ + extraKey1, Toast.LENGTH_LONG).show();

        // 중략

    }

참고사이트 : https://www.fun25.co.kr/blog/android-execute-3rdparty-app/?category=003

[Android] 안드로이드 앱에 버튼(Button) 추가

[Android] 안드로이드 앱에 버튼(Button) 추가

1. Activity 레이아웃 XML에 버튼(Button) 태그를 추가한다.

예를 들어 activity_main.xml 에 아래 내용을 추가한다.

<Button
    android:id=”@+id/button1″
    android:layout_width=”match_parent”
    android:layout_height=”wrap_content”
    android:text=”Button1″
    app:layout_constraintStart_toStartOf=”parent”
    app:layout_constraintTop_toTopOf=”parent” />

    <Button
    android:id=”@+id/button2″
    android:layout_width=”match_parent”
    android:layout_height=”wrap_content”
    android:layout_marginTop=”48dp”
    android:text=”Button2″
    app:layout_constraintStart_toStartOf=”parent”
    app:layout_constraintTop_toTopOf=”parent” />

2. 만약 버튼(Button) 태그에 빨간색 밑줄이 그어지면서 “This view is not constrained vertically: at runtime it will jump to the top unless you add a vertical constraint more… (Ctrl+F1)” 오류가 발생하는 경우.

Activity 레이아웃 xml(예를 들어 activity_main.xml 파일) 하단의 [Design] 탭을 클릭 – 상단의 마법봉 모양 아이콘(Infer Constraints) 버튼을 클릭한다.

3. Activity 자바 파일의 onCreate 메서드를 찾는다.

그 안에 findViewById 메서드로 버튼 변수를 선언하고, 이어서 온클릭 리스너(OnClickListener)를 구현한다.

아래 코드처럼 쓰고 “// 내용” 부분에 버튼 클릭 시 발생할 이벤트를 작성하면 된다.

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button1 = (Button) findViewById(R.id.button1);
        Button button2 = (Button) findViewById(R.id.button2);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 내용
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 내용
            }
        });
    }

참고사이트 : https://recipes4dev.tistory.com/54

[Windows] 확장 모니터가 인식되지 않는 문제 해결

[Windows] 확장 모니터가 인식되지 않는 문제 해결

HDMI 케이블로 연결해서 잘 쓰고 있던 확장모니터가 갑자기 인식되지 않는 문제가 있었다.

HDMI 케이블이 아닌 RGB 케이블로 연결해도 마찬가지였다.

사용한지 오래된 것도 아니었고 아무래도 하드웨어가 고장난 것은 아닌 것 같아서 검색을 통해 해결방법을 찾았다.

1. 우선 [시작(윈도우키)] – [장치 관리자]를 검색해서 장치 관리자 창을 연다.

2. 장치 관리자 창에서 [디스플레이 어댑터] 하위의 오류 이미지가 붙은 모니터를 제거해줘야 한다.

마우스 우클릭하여 [디바이스 제거] 한다.

 

3. 이어서 “디바이스 제거를 완료하려면 컴퓨터를 다시 시작해야 합니다. 지금 컴퓨터를 다시 시작할까요?” 메시지에 [예(Y)]를 선택해서 재시작한다. 재시작하기 전에 HDMI 케이블은 컴퓨터에서 연결 해제한다.

4. 재시작 이후 HDMI 선을 꽂았더니 모니터가 잘 인식되었다. 소프트웨어 문제였던 것 같다.

그래도 모니터 인식이 안된다면 그래픽 드라이버를 다시 설치해보자.

그래픽 드라이버 설치는 “3DP Chip Lite (3DP 칩 라이트)” 라는 이름의 프로그램을 추천한다.

사이트 https://www.3dpchip.com 에서 무료로 다운받을 수 있다.

위 방법으로 해결되지 않는다면 하드웨어 문제로 보인다. A/S를 받도록 하자.

참고사이트 : https://brunch.co.kr/@maama/398

[JAVA] ProcessBuilder 사용방법

[JAVA] ProcessBuilder 사용방법

자바에서 ProcessBuilder 로 특정 프로그램을 강제 실행할 수 있다.

아래 코드는 [시작] – [실행] 창에 “iexplore http://en.wikipedia.org/” 라고 입력했을 때의 결과와 같다.

    // 인터넷 익스플로러로 영문 위키피디아 사이트 접속하기
    try {
        ArrayList<String> list = new ArrayList();
        list.add(“C:\\Program Files\\Internet Explorer\\iexplore.exe”);
        list.add(http://en.wikipedia.org/);
            
        ProcessBuilder builder = new ProcessBuilder(list);
        builder.start();
    } catch (Exception e) {
        e.printStackTrace();
    }

ProcessBuilder 는 명령의 결과값도 리턴받을 수 있다.

예를 들어 아래 예제는 cmd 에서 dir 명령어를 한 결과를 가져온다.

        ProcessBuilder processBuilder = null;
        Process process = null;
        
        InputStreamReader inputReader = null;
        BufferedReader bufferedReader = null;
        
        ArrayList<String> valueList = null;
        
        try {
            processBuilder = new ProcessBuilder();
            valueList = new ArrayList<String>();
            valueList.add(“cmd”);
            valueList.add(“/C”);
            valueList.add(“dir”);
            
            // 특정 위치에서 실행 (dir 명령어를 특정 폴더에서 수행하고 싶을 경우)
            // processBuilder.directory(new File(“c:\\test”));
            
            processBuilder.redirectErrorStream(true);
            processBuilder.command(valueList);
            process = processBuilder.start();
            
            inputReader = new InputStreamReader(process.getInputStream(), “MS949”);
            bufferedReader = new BufferedReader(inputReader);
            
            String line = “”;
            while ((line = bufferedReader.readLine()) != null) {
                if (line.trim().length() == 0) {
                    continue;
                }
                
                System.out.println(line);
            }
            
            int resultValue = process.exitValue();
            System.out.println(“resultValue : “ + resultValue);
        
        } catch (Exception e) {
            e.printStackTrace();
            
        } finally {
            try {
                if (inputReader != null) {
                    inputReader.close();
                }
            } catch (Exception e) {
            } finally {
                inputReader = null;
            }
            
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (Exception e) {
            } finally {
                bufferedReader = null;
            }
            
            try {
                if (process != null) {
                    process.destroy();
                }
            } catch (Exception e) {
            } finally {
                process = null;
            }
        }

여기서 processBuilder.directory() 부분은 검색해도 잘 안나오는 내용인데, 특정 폴더 위치에서 프로세스 빌더를 실행하고 싶을 때 사용하면 된다(프로세스 빌더의 작업 디렉토리 지정).

[JAVA] 멀티 스크린(다중 디스플레이)을 고려한 스크린샷 찍기

[JAVA] 멀티 스크린(다중 디스플레이)을 고려한 스크린샷 찍기

자바에서 현재 화면(스크린샷)을 BufferedImage 객체로 만드는 코드는 다음과 같다.

    private static BufferedImage getScreenShotImage() {
        BufferedImage screenImgObj = null;
        
        try {

            // 해상도 구하기
            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

            // 스크린샷 찍기
            Robot robot = new Robot();
            screenImgObj = robot.createScreenCapture(new Rectangle(0, 0, (int) screenSize.getWidth(), (int) screenSize.getHeight()));

            // 스크린샷 파일화
            // ImageIO.write(screenImgObj, “bmp”, new File(“c:/test/screenshot.bmp”));

        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return screenImgObj;
    }

하지만 위 코드는 모니터가 1개일 때만 잘 작동한다.

모니터가 2개 이상(멀티 스크린, 다중 디스플레이)일 경우 스크린샷을 찍는 코드는 다음과 같다.

화면 하나는 BufferedImage 1개가 되므로 n개의 BufferedImage 가 나온다.

    private static ArrayList<BufferedImage> getScreenShotImageForMultiScreen() {

        ArrayList<BufferedImage> screenImgObjList = new ArrayList<BufferedImage>();
        BufferedImage screenImgObj = null;
        
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] gs = ge.getScreenDevices();
        for (GraphicsDevice curGs : gs) {
            GraphicsConfiguration[] gc = curGs.getConfigurations();
            for (GraphicsConfiguration curGc : gc) {

                // 해상도 구하기
                Rectangle bounds = curGc.getBounds();

                try {
                    // 스크린샷 찍기
                    Robot robot = new Robot();
                    screenImgObj = robot.createScreenCapture(new Rectangle((int) bounds.getX(), (int) bounds.getY(), (int) bounds.getWidth(), (int) bounds.getHeight()));

                    if (screenImgObj != null) {
                        screenImgObjList.add(screenImgObj);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        return screenImgObjList;
    }

[Android] 안드로이드 스튜디오 Hello World

[Android] 안드로이드 스튜디오 Hello World

1. 안드로이드 스튜디오(Android Studio) 를 실행한다.

 

 

2. [Start a new Android Studio project] 메뉴를 클릭한다.

 

3. [Empty Activity] 를 선택하고, 하단의 [Next] 버튼을 클릭한다.

 

4. Name 에는 Hello World 입력

Package name 에는 com.본인닉네임.hello 를 입력

Save location 에는 원하는 경로를 입력한다. 여기서는 C:\AndroidWorkspaces\HelloWorld 로 했으며 C:\ 밑의 AndroidWorkspaces 폴더는 직접 생성해야 오류가 발생하지 않는다. (최종폴더 상위까지는 폴더를 미리 만들어두면 됨)

 

5. 프로젝트가 켜지면 모두 빌드될 때까지 차분히 기다린다. (중간에 종료하면 손상되므로 처음부터 다시 해야함)

우측 하단의 프로그레스 상태를 지켜보면 된다.

 

6. 프로젝트 빌드가 완료되면 USB 선으로 안드로이드 핸드폰을 컴퓨터에 연결한다.

안드로이드 스튜디오 우측 상단의 ▶(화살표 버튼)을 클릭한다.

 

7. 기기 선택 윈도우가 뜨면 Connected Devices 에 있는 본인 핸드폰을 선택하고 [OK] 버튼을 클릭한다.

핸드폰을 컴퓨터에 연결하지 않은 경우 Connected Devices 목록에 핸드폰이 뜨지 않는다.

아래 Available Virtual Devices 는 가상 기기이므로 속도가 너무 느리기 때문에 추천하지 않는다.

 

8. 기다리면 연결한 핸드폰 화면에 Hello World! 가 표시되어 있다.


[Windows] ocx을(를) 로드했지만 오류 코드 0x80040201(으)로 인해 DllRegisterServer 호출에 실패했습니다.

[Windows] ocx을(를) 로드했지만 오류 코드 0x80040201(으)로 인해 DllRegisterServer 호출에 실패했습니다.

명령 프롬프트(cmd) 창에서 REGSVR32.EXE “C:\OCX경로\파일명.ocx” 실행했을 때, 아래 오류 발생하는 경우.

모듈 “C:\OCX경로\파일명.ocx”을(를) 로드했지만 오류 코드 0x80040201(으)로 인해 DllRegisterServer 호출에 실패했습니다.

이 문제에 대한 자세한 내용을 보려면 오류 코드를 검색 단어로 사용하여 온라인에서 검색하십시오.

해결책은 [명령 프롬프트(cmd)] 아이콘 마우스 우클릭 – [관리자 권한으로 실행] 해서 REGSVR32.EXE “C:\OCX경로\파일명.ocx” 를 다시 실행해본다.

[SQL] SQL 프로시저 실행하는 리눅스 쉘(sh) 작성

[SQL] SQL 프로시저 실행하는 리눅스 쉘(sh) 작성

리눅스 환경에서 sqlplus로 DB에 접속 가능해야 한다.

아래와 같이 sh 파일을 작성하고 실행하면 SQL 프로시저가 실행된다.

여기서는 프로시저 명을 SP_TEST 로 가정했다.

export ORACLE_BASE=/oracle/app/oracle

export ORACLE_SID=TEST

export ORACLE_HOME=/oracle/app/oracle/product/10.2.0

export NLS_LANG=american_america.K016KSC5601

export PATH=$PATH:$ORACLE_HOME/bin:/usr/local/bin:/usr/bin

sqlplus user/password <<!

alter session set NLS_DATE_FORMAT = ‘YYYY-MM-DD’;

exec SP_TEST;

exit

!

[Javascript] js remove iframe (js removeChild 사용방법)

[Javascript] js remove iframe (js removeChild 사용방법)

document.getElementById 로 가져온 HTML element 를 제거하는 방법.

removeChild 함수를 쓰면 된다.

var targetObj = document.getElementById(“target_iframe_id”);
if (targetObj != null && targetObj.parentNode != null) {
    targetObj.parentNode.removeChild(targetObj);
}

[JAVA] 제네릭이 String 인 ArrayList 정렬하기 (ArrayList sort / String sort)

[JAVA] 제네릭이 String 인 ArrayList 정렬하기 (ArrayList sort / String sort)

문자열 “aaaa”, “aaa”, “aaab”, “aab” 를 정렬(오름차순 정렬)하면 다음과 같다.

[aaa, aaaa, aaab, aab]

문자열 “aaaa”, “aaa”, “aaab”, “aab” 를 역정렬(내림차순 정렬)하면 다음과 같다.

[aab, aaab, aaaa, aaa]

1. 문자열(String)의 오름차순 정렬​ (간단한 방법)

자바에서 ArrayList 를 정렬(오름차순으로 정렬)하려면 Collections.sort 메서드를 사용하면 된다.

제네릭(Generic)이 String 인 ArrayList 도 마찬가지다.

ArrayList<String> strList = new ArrayList<String>();

strList.add(“aaaa”);
strList.add(“aaa”);
strList.add(“aaab”);
strList.add(“aab”);

// 리스트를 오름차순으로 정렬
Collections.sort(strList);
System.out.println(strList);

결과

[aaa, aaaa, aaab, aab]

2. 문자열(String)의 내림차순 정렬​​ (간단한 방법)

ArrayList 를 역정렬(내림차순으로 정렬)하려면 Collections.sort 메서드를 사용하고, 이어서 Collections.reverse 메서드를 사용하면 된다.


ArrayList<String> strList = new ArrayList<String>();

strList.add(“aaaa”);
strList.add(“aaa”);
strList.add(“aaab”);
strList.add(“aab”);

// 리스트를 내림차순으로 정렬
Collections.sort(strList);

Collections.reverse(strList);
System.out.println(strList);

결과

[aab, aaab, aaaa, aaa]

3. 문자열(String)의 오름차순 정렬​ (Comparator 인터페이스의 compare 메서드를 직접 구현)

Comparator 인터페이스의 compare 메서드를 직접 구현해서 정렬하는 방법도 있다.

제네릭이 String 또는 Integer 일 때는 그다지 쓸모없어보이는데, 특수한 정렬 규칙을 적용하고 싶다거나 제네릭이 직접 만든 클래스일 경우 사용하면 좋다.

compare 메서드를 구현(임플리먼트)할 때, 0 을 리턴하면 순서가 바뀌지 않는다.

두 값이 같다면 0을 반환하고, 첫 번째 값이 크다면 양수(1)를, 두 번째 인자가 크다면 음수(-1)를 반환하면 된다.

 

    public static void main(String[] args) {

        ArrayList<String> strList = new ArrayList<String>();

        strList.add(“aaaa”);
        strList.add(“aaa”);
        strList.add(“aaab”);
        strList.add(“aab”);

        // 리스트를 오름차순으로 정렬
        Collections.sort(strList, new Comparator<String>() {

            @Override
            public int compare(String s1, String s2) {
                int result = 0;
                
                if (s1 == null) {
                    s1 = “”;
                }
                
                if (s2 == null) {
                    s2 = “”;
                }
                
                int len1 = s1.length();
                int len2 = s2.length();
                int len = (len1 < len2) ? len1 : len2;
                for (int i=0; i<len; i++) {
                    if (s1.charAt(i) > s2.charAt(i)) {
                        result = 1;
                        break;
                    } else if (s1.charAt(i) < s2.charAt(i)) {
                        result = -1;
                        break;
                    }
                }
                
                if (result == 0) {
                    if (len1 > len2) {
                        result = 1;
                    } else if (len1 < len2) {
                        result = -1;
                    }
                }
                
                return result;
            }
        });
        
        System.out.println(strList);
    }

결과

[aaa, aaaa, aaab, aab]

4. 문자열(String)의 내림차순 정렬​ (Comparator 인터페이스의 compare 메서드를 직접 구현)

항목 3번의 코드에서 부등호 방향만 바꿔주면 된다.

    public static void main(String[] args) {

        ArrayList<String> strList = new ArrayList<String>();

        strList.add(“aaaa”);
        strList.add(“aaa”);
        strList.add(“aaab”);
        strList.add(“aab”);

        // 리스트를 오름차순으로 정렬
        Collections.sort(strList, new Comparator<String>() {

            @Override
            public int compare(String s1, String s2) {
                int result = 0;
                
                if (s1 == null) {
                    s1 = “”;
                }
                
                if (s2 == null) {
                    s2 = “”;
                }
                
                int len1 = s1.length();
                int len2 = s2.length();
                int len = (len1 < len2) ? len1 : len2;
                for (int i=0; i<len; i++) {
                    if (s1.charAt(i) < s2.charAt(i)) {
                        result = 1;
                        break;
                    } else if (s1.charAt(i) > s2.charAt(i)) {
                        result = -1;
                        break;
                    }
                }
                
                if (result == 0) {
                    if (len1 < len2) {
                        result = 1;
                    } else if (len1 > len2) {
                        result = -1;
                    }
                }
                
                return result;
            }
        });
        
        System.out.println(strList);
    }

결과

[aab, aaab, aaaa, aaa]

#Java 문자열 정렬

#Java 문자열 쏘트

#Java 문자열 Sort

#Java 스트링 정렬

#Java 스트링 쏘트

#Java 스트링 Sort

#Java String 정렬

#Java String 정렬

#Java String 쏘트

#자바 문자열 정렬

#자바 문자열 쏘트

#자바 문자열 Sort

#자바 스트링 정렬

#자바 스트링 쏘트

#자바 스트링 Sort

#자바 String 정렬

#자바 String 정렬

#자바 String 쏘트

[iOS] 애플 푸시 인증서 갱신

[iOS] 애플 푸시 인증서 갱신

애플 푸시 인증서 갱신을 만료되기 한 달 전에 미리 해뒀었는데, 안심하고 있다가 아이폰에 푸시 알림이 오지 않는 문제가 보고됐다.

푸시중계서버 로그를 보니 javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_expired 오류가 발생되고 있었다.

앱을 새로 파일할 때 푸시 인증서를 갱신하는 것까지는 좋았는데, 모바일 서버의 푸시중계서버(WAS) 내에 들어있는 확장자 .p12 파일을 교체해주지 않았기 때문이었다.

테스트할 때도 문제를 발견하지 못했다. 해당 .p12 파일을 교체하지 않았어도 만료시점이 한 달 정도 남아있었기 때문이다.

앞으로 만료시점이 다가온 애플 푸시 인증서를 재발급했을 때는, 모바일 서버 내에 들어있는 .p12 파일도 교체해줘야 함을 잊지 말아야겠다.

관련해서 아래 2개의 포스트를 작성했다.

[JAVA] javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_expired (https://blog.naver.com/bb_/222033555014)

[JAVA] com.notnoop.exceptions.InvalidSSLConfig: Given final block not properly padded (https://blog.naver.com/bb_/222033571452)

[JAVA] com.notnoop.exceptions.InvalidSSLConfig: Given final block not properly padded

[JAVA] com.notnoop.exceptions.InvalidSSLConfig: Given final block not properly padded

자바 APNS(Apple Push Notifications Service) 푸시 연동 코드에서 아래 오류가 발생한 경우

com.notnoop.exceptions.InvalidSSLConfig: java.io.IOException: failed to decrypt safe contents entry: javax.crypto.BadPaddingException: Given final block not properly padded

(중략)

Caused by: java.io.IOException: failed to decrypt safe contents entry: javax.crypto.BadPaddingException: Given final block not properly padded

(중략)

Caused by: javax.crypto.BadPaddingException: Given final block not properly padded

애플 푸시 인증서의 비밀번호가 틀린 경우였다.

맥OS 키체인에서 새로 발급받은(또는 만료가 되지 않은) 푸시 인증서를 .p12  확장자 파일로 내보내기해서 가져올 때 비밀번호를 설정하라는 메시지가 표시되는데, 이때 입력한 메시지와 자바 클래스에서 인증서에 접근할 때 사용하는 비밀번호가 불일치하는 경우였다.

확장자 .p12 인증서를 올바른 비밀번호로 다시 내보내기하고, 기존 자바 클래스가 바라보는 .p12파일을 새로 가져온 .p12 파일로 교체해서 해결했다.

[JAVA] javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_expired

[JAVA] javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_expired

자바 APNS(Apple Push Notifications Service) 푸시 연동 코드에서 아래 오류가 발생한 경우

com.notnoop.exceptions.NetworkIOException: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_expired

(중략)

Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert : certificate_expired

애플 푸시 인증서가 만료된 경우였다.

맥OS 키체인에서 새로 발급받은(만료가 되지 않은) 푸시 인증서를 .p12  확장자 파일로 내보내기해서 가져왔다.

이후 기존 자바 클래스가 바라보는 .p12파일을 새로 가져온 .p12 파일로 교체해서 해결했다.

[iOS] 아이폰 UDID 가져오기 / UDID 알아내는 방법

[iOS] 아이폰 UDID 가져오기 / UDID 알아내는 방법

아이폰 앱을 개발하면서 애플 개발자 센터에 디바이스를 등록하기 위해 특정 아이폰의 UDID가 필요한 경우가 많다.

아이폰 UDID는 다음과 같은 방법으로 알아낼 수 있다.

1. 아이폰으로 http://get.udid.io/ 에 접속한다. 웹페이지가 뜨면 [Tap to find UDID] 버튼을 클릭한다.

2. “이 웹사이트가 구성 프로파일을 다운로드하려고 합니다. 이 동작을 허용하겠습니까?” 메시지가 나오면 [허용] 버튼을 클릭한다.

3. 프로파일이 다운로드 되었으면, 아이폰 홈화면에서 [설정] 아이콘을 클릭한다.

4. 설정 화면에서 [일반] 메뉴를 클릭한다.


 

5. 일반 화면에서 [프로파일] 메뉴를 클릭한다.

 

6. [Get Your UDID] 를 클릭한다.

7. 우측 상단의 [설치] 텍스트를 클릭한다.

 

8. 하단에 [설치], [취소] 선택지가 표시되면 [설치] 텍스트를 클릭한다.

 

9. 자동으로 웹페이지가 뜨며 UDID가 표시된다.

(만약 UDID가 표시되지 않는다면, 다시 시도해본다. 다시 시도해도 계속 UDID가 뜨지 않는다면, [개인정보보호] 모드일 수 있다. 아이폰 개인정보보호 모드를 해제하고 다시 시도해본다.)

[Send UDID via E-mail] 버튼을 눌러서 이메일로 보낼 수도 있지만,

로그인 과정이 번거로우므로 그냥 스크린샷 찍는게 깔끔하다.

[JAVA] SWT 기본 윈도우 예제

[JAVA] SWT 기본 윈도우 예제

자바에서 위 이미지처럼 윈도우를 띄우는 예제코드.

1. 자바 프로젝트에 SWT 라이브러리를 임포트한다. (ex : org.eclipse.swt.win32.win32.x86_64-4.3.jar)

2. 아래 코드를 붙여넣는다.

package com.bb.test;

import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class SWTtest {
    public static boolean bInit = false;
    public static Text textBox1 = null;

    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText(“Window Text”);
        shell.setLayout(new Layout() {

            @Override
            protected void layout(Composite arg0, boolean arg1) {
                // 윈도우 초기 사이즈 설정
                if (!bInit) {
                    arg0.setSize(800, 600);
                    bInit = true;
                }

                // 윈도우 크기에 따라 텍스트박스 사이즈 조절
                try {
                    if (textBox1 != null) {
                        textBox1.setBounds(10, 50, arg0.getSize().x – 40, arg0.getSize().y – 110);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            protected Point computeSize(Composite arg0, int arg1, int arg2, boolean arg3) {
                return null;
            }
        });

        // 버튼 생성
        Button button1 = new Button(shell, 0);
        button1.setBounds(10, 10, 150, 30);
        button1.setText(“Button Text”);

        // 텍스트박스 생성
        textBox1 = new Text(shell, 2626);
        textBox1.setLayoutData(new GridData(1808));
        textBox1.setFont(new Font(display, “굴림”, 20, 0));
        textBox1.setText(“Hello World”);
        textBox1.setFocus();

        // 레이블 생성
        Label label1 = new Label(shell, 0);
        label1.setBounds(200, 10, 150, 30);
        label1.setText(“Label Text”);

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}

[JAVA] SWT 라이브러리

[JAVA] SWT 라이브러리

java에서 GUI를 지원하기 위한 기본 패키지로 Swing이 있다.

이 Swing을 대체하기 위해 만들어진 GUI 라이브러리가 SWT다. SWT는 네이티브에 가까운 GUI를 제공하여 결론적으로 Swing보다 나은 GUI를 사용할 수 있다.

첨부한 org.eclipse.swt.win32.win32.x86_64-4.3.jar 파일을 임포트하면 된다.

해당 파일은 jdk 1.6 이상에서 구동 가능하다. (최신 버전의 경우 jdk1.7 이상을 요구함)

출처 : https://mvnrepository.com/artifact/org.eclipse.swt/org.eclipse.swt.win32.win32.x86_64/4.3

[IE11] 특정PC 에서 IE 드래그 앤 드랍 안되는 현상

[IE11] 특정PC 에서 IE 드래그 앤 드랍 안되는 현상

인터넷 익스플로러 11(Internet Explorer 11)에서 첨부하려고 파일을 드래그하면 마우스 커서가 금지표시(⊘)로 바뀌며 첨부가 안되는 현상(Drag&Drop 안되는 현상).

다른 PC에서는 괜찮은데 특정 PC에서만 재현될 경우(소스 코드 문제가 아닐 경우) 권한 문제일 수 있다.

IE를 관리자 권한으로 실행한 경우, 윈도우 탐색기의 권한과 맞지 않아 드래그 앤 드랍을 할 수 없다. 해결방법은 IE를 관리자 권한이 아닌 일반 권한으로 실행하면 된다.

■ IE를 일반 권한으로 실행하는 방법 (IE와 윈도우 탐색기 둘 다 일반 권한으로 실행)

인터넷 익스플로러 바로가기 아이콘 위에서 마우스 우클릭 – [속성(R)] 클릭 – [바로가기 탭] [고급(D)…] – [관리자 권한으로 실행(R)] 체크박스 해제 – [확인] 버튼 클릭

이후 IE를 다시 실행해본다.

위 방법으로 안되면 아래 방법을 사용한다.

■ 윈도우 탐색기를 관리자 권한으로 실행하는 방법 (IE와 윈도우 탐색기 둘 다 관리자 권한으로 실행

Ctrl + Shift + Delete 로 작업관리자 열기 – 상단 메뉴의 [파일(F)] – [새 작업 실행(N)] – 인풋박스에 explorer 라고 입력하고 [관리자 권한으로 이 작업 실행] 체크박스 체크 – [확인] 버튼 클릭

참고사이트 : https://w3tech.tistory.com/227

[JAVA] 자바 jar 파일 압축해제 소스코드

[JAVA] jar 파일 압축해제 소스코드

혹시 JAVA 소스코드를 보러 온게 아니라 jar 파일을 압축풀고 싶으신 분을 위해서 적어둠.

jar 파일은 zip 파일과 구조가 같으므로 반디집, 알집 같은 툴로 압축해제하면 된다.

아래는 jar 파일을 압축해제하는 자바 소스코드다.

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

public class JarExtractUtil {

    public static void extractJar(String jarFilePath, String destinationDirPath) throws NullPointerException, Exception {
        File f =
null;
        JarFile jarFile =
null;
        
        try {

            f = new File(jarFilePath);
            jarFile =
new JarFile(f);
            File destDir =
new File(destinationDirPath);
            if (!destDir.exists()) {
                destDir.mkdirs();
            }
    
            Enumeration entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();
                if (entry.isDirectory()) {
                    File newDir =
new File(destDir, entry.getName());
                    if (entry.getName().toUpperCase().indexOf(“META-INF”) > -1) {
                        continue;
                    }
                    
                    newDir.mkdirs();
                    
                }
else {
                    if (entry.getName().toUpperCase().indexOf(“META-INF”) > -1) {
                        continue;
                    }
    
                    File targetFileObj =
new File(destDir, entry.getName());
                    
                    InputStream input =
null;
                    FileOutputStream fileOutput =
null;
                    BufferedOutputStream buffOutput =
null;
    
                    try {
                        input = jarFile.getInputStream(entry);
                        fileOutput =
new FileOutputStream(targetFileObj);
                        buffOutput =
new BufferedOutputStream(fileOutput);
                        byte[] buf =
new byte[4096];
                        int read = -1;
                        while ((read = input.read(buf)) != -1) {
                            buffOutput.write(buf, 0, read);
                        }
                        buffOutput.flush();
                        buffOutput.close();
    
                        fileOutput.close();
                        input.close();
    
                    }
catch (IOException e) {
                        throw e;
                    }
catch (Exception e) {
                        throw e;
    
                    }
finally {
                        try {
                            if (buffOutput != null) {
                                buffOutput.close();
                            }
                        }
catch (IOException e) {
                        }
catch (Exception e) {
                        }
finally {
                            buffOutput =
null;
                        }
    
                        try {
                            if (fileOutput != null) {
                                fileOutput.close();
                            }
                        }
catch (IOException e) {
                        }
catch (Exception e) {
                        }
finally {
                            fileOutput =
null;
                        }
    
                        try {
                            if (input != null) {
                                input.close();
                            }
                        }
catch (IOException e) {
                        }
catch (Exception e) {
                        }
finally {
                            input =
null;
                        }
                    }
                }
            }
        }
catch (IOException e) {
            throw e;
        }
catch (Exception e) {
            throw e;
        }
finally {
            try {
                if (jarFile != null) {
                    jarFile.close();
                }
            }
catch (IOException e) {
            }
catch (Exception e) {
            }
finally {
                jarFile =
null;
            }
        }
    }
}

참고사이트 : http://www.gnujava.com/board/article_view.jsp?article_no=371&board_no=1&table_cd=EPAR01&table_no=01

[JAVA] class file has wrong version 50.0, should be 49.0

[JAVA] class file has wrong version 50.0, should be 49.0

아래 오류가 발생했다.

class file has wrong version 50.0, should be 49.0
Please remove or make sure it appears in the correct subdirectory of the classpath.

클래스 버전은 version 50.0 (jdk 1.6) 인데, 실행한 환경은 version 49.0 (jdk 1.5) 인 경우다.

참고로 version 값과 jdk 버전의 관계는 다음과 같다.

version 45.3 = jdk 1.1
version 46 = jdk 1.2
version 47 = jdk 1.3
version 48 = jdk 1.4
version 49 = jdk 1.5
version 50 = jdk 1.6
version 51 = jdk 1.7
version 52 = jdk 1.8
version 53 = jdk 1.9

해결책은

(1) 클래스 컴파일 버전을 jdk 1.5 로 낮춰서 다시 컴파일하거나

(2) 실행 환경의 jdk 버전을 1.6 으로 올리는 방법이 있다.

로컬 테스트 환경에서 위 오류가 발생했다면 어떤 해결책을 선택해도 괜찮다.

그런데 실제 운영 환경에서 위 오류가 발생했다면, 실행 환경의 jdk 버전을 변경하는 것은 부담스러우므로 클래스 컴파일 버전을 바꿔서 다시 컴파일하는 편이 안전해보인다.

참고사이트 : https://gkflqkfl.tistory.com/138

[JAVA] java.lang.UnsupportedClassVersionError: Bad version number in .class file

[JAVA] java.lang.UnsupportedClassVersionError: Bad version number in .class file

아래 오류가 발생했다.

java.lang.UnsupportedClassVersionError: Bad version number in .class file (unable to load class org.apache.commons.codec.binary.Base64)

UnsupportedClassVersionError 는 클래스들 간 서로 컴파일 버전이 맞지 않는 경우 발생하는 오류다.

commons-codec-1.8.jar 파일 안에 있는 org.apache.commons.codec.binary.Base64 패키지의 class 를 로드하다가 오류가 난 것이다.

확인 결과 commons-codec-1.8.jar 는 jdk1.6 으로 컴파일된 jar 였고, 내가 작업하는 프로젝트의 컴파일 버전은 jdk1.5 였다.

해결책은

(1) 사용하는 jar에 맞게 프로젝트의 클래스 컴파일 버전을 높이거나

(2) 프로젝트의 클래스 컴파일 버전에 맞게 사용하는 jar의 버전을 낮추면 된다.

commons-codec-1.8.jar 파일을 commons-codec-1.3.jar 로 교체하여 해결함.

참고로 commons-codec-1.3.jar 는 jdk1.2 로 컴파일된 jar 이다.

[JAVA] 파일 인코딩 타입 가져오기

[JAVA] 파일 인코딩 타입 가져오기

필요 라이브러리 : juniversalchardet-1.0.3.jar

    /**
     * 파일 인코딩 타입을 찾는다.
     *
     * @param file
     * @return
     * @throws IOException
     * @throws Exception
     */
    public static String detectCharset(File file) throws IOException, Exception {
        if (file == null || !file.exists()) {
            return null;
        }
       
        String result = “”;

        FileInputStream inputStream = null;

        try {
            inputStream = new FileInputStream(file);
            result =  detectCharset(inputStream);
           
        } catch (IOException e) {
            throw e;

        } catch (Exception e) {
            throw e;

        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (NullPointerException e) {
            } catch (Exception e) {
            }
        }

        return result;
    }
   
   
    /**
     * 파일 인코딩 타입을 찾는다.
     *
     * @param inputStream
     * @return
     * @throws NullPointerException
     * @throws Exception
     */
    public static String detectCharset(InputStream inputStream) throws NullPointerException, Exception {

        int nread = 0;
        String encoding = “”;
        try {
            byte[] buf = new byte[1024];
            UniversalDetector detector = new UniversalDetector(null);

            while ((nread = inputStream.read(buf)) > 0 && !detector.isDone()) {
                detector.handleData(buf, 0, nread);
            }
            detector.dataEnd();

            encoding = detector.getDetectedCharset();
            if (encoding == null) {
                encoding = “”;
            }

            detector.reset();
       
        } catch (NullPointerException e) {
            throw e;
           
        } catch (Exception e) {
            throw e;
           
        } finally {
        }
       
        return encoding;
    }

[JAVA] 특정 URL의 이미지스트림 가져오기 (Image URL to OutputStream)

[JAVA] 특정 URL의 이미지스트림 가져오기 (Image URL to OutputStream)

이미지 URL을 아웃풋스트림으로 내려주기 위한 코드.

꼭 이미지가 아니더라도 스트림을 그대로 내려주기 위한 코드이다.

WAS가 DMZ에 있는 경우 해당 WAS를 통하지 않고는 이미지를 가져올 수 없을 때 사용.

WAS의 특정 URL을 호출했을 때 실제로는 변수 strUrl에 해당하는 이미지를 내려주는 코드이다.

public void getImgFromUrl(HttpServletResponse response, String strUrl) {
        if (strUrl == null || strUrl.length() == 0) {
            return;
        }
        
        InputStream inputStream = null;
        ServletOutputStream outputStream = null;
        
        try {
            inputStream = new URL(strUrl).openStream();
            outputStream = response.getOutputStream();
            
            int length;
            byte[] buffer = new byte[12288]; // 12K
            while ((length = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, length);
            }

              outputStream.flush();
            
        } catch (Exception e) {
            e.printStackTrace();
            
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (NullPointerException e) {
            } catch (Exception e) {
            }
            
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (NullPointerException e) {
            } catch (Exception e) {
            }
        }
    }

참고사이트 : https://stackoverflow.com/questions/8623709/output-an-image-file-from-a-servlet

[Apache] Init: SSLPassPhraseDialog builtin is not supported on Win32

[Apache] Init: SSLPassPhraseDialog builtin is not supported on Win32

아파치 logs 폴더 안의 error.log 파일에 다음과 같이 에러 로그가 찍혔을 때.

[error] Init: SSLPassPhraseDialog builtin is not supported on Win32 (key file C:/Apache2.2/conf/server.key)

리눅스에서 아래 명령어를 입력하고 서버를 재기동한다. 혹시 모르니 파일을 미리 백업하고 진행하자.

openssl rsa -in 파일명.key -out 파일명.key
ex) openssl rsa -in server.key -out server.key

참고사이트: https://groove2u.tistory.com/entry/아파치-서버-SSL설정법

[Eclipse] Errors occurred during the build. Errors running builder ‘Validation’ on project

[Eclipse] Errors occurred during the build. Errors running builder ‘Validation’ on project

이클립스 상단메뉴 [Project] – [Clean…] 실행했을 때 아래 오류가 발생하는 경우.

Errors occurred during the build.
Errors running builder ‘Validation’ on project ‘[프로젝트명]’.
java.lang.NullPointerException

해결책은 [워크스페이스 경로]\.metadata\.plugins\org.eclipse.wst.validation 폴더를 삭제하고 다시 Clean하면 된다.

참고사이트 : https://rosebay.tistory.com/53

[JAVA] HttpUrlConnection 한글 깨짐 해결

[JAVA] HttpUrlConnection 한글 깨짐 해결

원하는 페이지 주소를 직접 입력했을 때 한글이 깨지지 않고 정상 출력되지만,

HttpURLConnection 객체를 사용했을 때 한글이 깨지는 경우.

InputStreamReader inputStreamReader = new InputStreamReader(inputStream, “UTF-8”); 코드를 사용하면 된다.

package com.bb.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class MainClass {

    publicstaticvoid main(String[] args) {

        URL url = null;
        HttpURLConnection urlConnection =
null;
        InputStream inputStream =
null;
        InputStreamReader inputStreamReader =
null;
        BufferedReader bufferedReader =
null;

        try {
            url =
new URL(http://원하는페이지주소);
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setConnectTimeout(1000);

              urlConnection.setReadTimeout(2000);


            inputStream = (InputStream) urlConnection.getContent();
            inputStreamReader =
new InputStreamReader(inputStream, “UTF-8”);
            bufferedReader =
new BufferedReader(inputStreamReader);

            StringBuffer buff = new StringBuffer();

            String oneLine = null;
            while ((oneLine = bufferedReader.readLine()) != null) {
                if (oneLine == null || oneLine.length() == 0) {
                    continue;
                }

                buff.append(oneLine);
            }

            System.out.println(“result : “ + buff.toString());

        } catch (NullPointerException e) {
            e.printStackTrace();

        } catch (Exception e) {
            e.printStackTrace();

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

            try {
                if (inputStreamReader != null) {
                    inputStreamReader.close();
                }
            }
catch (NullPointerException e) {
            }
catch (Exception e) {
            }
finally {
                inputStreamReader =
null;
            }

            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            }
catch (NullPointerException e) {
            }
catch (Exception e) {
            }
finally {
                inputStream =
null;
            }

            try {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
            }
catch (NullPointerException e) {
            }
catch (Exception e) {
            }
finally {
                urlConnection =
null;
            }
        }
    }
}

참고사이트 : https://sarc.io/index.php/development/1444-httpurlconnection-utf-8

[Javascript] js 아이폰인지 안드로이드인지 검사

[Javascript] js 아이폰인지 안드로이드인지 검사

function getMobileType() {
    var strUserAgent = navigator.userAgent.toLowerCase();
    if (strUserAgent != null) {
        if (strUserAgent.indexOf(“android”) > -1) {
            return “android”;
       
        } else if (strUserAgent.indexOf(“iphone”) > -1 || strUserAgent.indexOf(“ipad”) > -1 || strUserAgent.indexOf(“ipod”) > -1 || strUserAgent.indexOf(“mac os”) > -1 || strUserAgent.indexOf(“safari”) > -1) {
            return “ios”;
        }
    }

   

    return “”;
}

참고사이트 : https://justbobby.tistory.com/20

PsTools

PsTools
Windows NT 및 Windows 2000 Resource Kit에는 Windows NT / 2K 시스템을 관리하는 데 도움이 되는 많은 커맨드라인 도구가 제공됩니다. 시간이 지남에 따라 Resource Kit에 포함되지 않은 도구를 포함하여 유사한 도구 모음을 개발했습니다. 차별점은 원격 시스템과 로컬 시스템을 모두 관리할 수 있다는 것입니다. 제품군의 첫 번째 도구는 프로세스에 대한 자세한 정보를 볼 수 있는 도구인 PsList였으며 제품군은 지속적으로 성장하고 있습니다. PsList의 “Ps”접두어는 표준 UNIX 프로세스 나열 커맨드라인 도구의 이름이 “ps”라는 사실과 관련이 있으므로 모든 도구에 이 접두어를 채택하여 이를 도구 모음으로 묶었습니다.
• PsExec-프로세스를 원격으로 실행

• PsFile-원격으로 열린 파일을 표시

• PsGetSid-컴퓨터 또는 사용자의 SID를 표시

• PsInfo-시스템에 대한 정보를 나열

• PsPing-네트워크 성능 측정

• PsKill-이름 또는 프로세스 ID로 프로세스 종료

• PsList-프로세스에 대한 자세한 정보를 나열

• PsLoggedOn-로컬 및 리소스 공유를 통해 누가 로그온했는지 확인 (전체 소스 포함)

• PsLogList-이벤트 로그 레코드 덤프

• PsPasswd-계정 비밀번호 변경

• PsService-서비스보기 및 제어

• PsShutdown-컴퓨터를 종료하고 선택적으로 재부팅

• PsSuspend-프로세스를 일시 중단

• PsUptime-마지막 재부팅 이후 시스템이 얼마나 오래 실행되었는지 보여줌 (PsUptime의 기능이 PsInfo에 통합되었습니다)

[Unity] Unity 기초강의 내용 정리 (5-3강 ~ 5-4강)

[Unity] Unity 기초강의 내용 정리 (5-3강 ~ 5-4강)

educast 나동빈 님의 <Mobile Defence Game 제작으로 배우는 Unity 기초> 강의를 듣고 내용 정리.

———-

 

5-3. 타워 건설 지점 오브젝트 구현하기

타워가 건설이 되는 위치 만들기

건설 지점을 눌렀을 때 씨앗을 차감하고 타워를 건설

1. Prefabs 폴더 생성

빈 오브젝트 만들고 이름을 Tower Spots 로 수정 (X : 0, Y : 0, Z : 0)

Sprites 폴더로부터 새로운 타일(tile_1)을 씬뷰에 드래그앤드랍한다.

Tower Spots 자식 요소로 넣는다.

X : -5, Y : 2, Z : 0

좌측 하이어라키 뷰의 tile_1을 프로젝트 뷰의 프리팹 폴더 안에 드래그하면 프리팹이 생성된다.

이름은 Tower Spot 로 변경.

Sorting Layer 를 background 로 지정하기

2. 가로 7개, 세로 4개 총 28개의 타일을 씬뷰에 붙여넣기

28개 오브젝트의 이름을 모두 Tower Spot 로 변경.

Sorting Layer 를 background 로 지정하기

28개 오브젝트 모두 좌측 하이어라키 뷰 상 Tower Spots 객체의 자식요소로 넣기

———-

5-4. 캐릭터 타워 건설 기능과 능력치 구현하기

당근 캐릭터 씬뷰에 드래그앤드랍

크기는 Scale 0.2, 0.2

Sorting Layer는 Unit 으로 지정

이 당근 캐릭터를 프리팹 폴더 안에 드래그앤드랍해서 프리팹으로 만들자. (Character 1)

Scripts 폴더 만들고

CreateCharacter 라는 스크립트 생성

타일 한 개에 Add Component 를 이용해서

CreateCharacter 스크립트 부여

박스 콜라이더 2D도 추가 (충돌처리 위함)

소스코드 작성

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class CreateCharacter : MonoBehaviour {

    public GameObject characterPrefab;

    private GameObject character;

 // Use this for initialization

 void Start () {

  

 }

 

 // Update is called once per frame

 void Update () {

  

 }

    private void OnMouseDown() {

        if (character == null) {

            character = (GameObject)Instantiate(characterPrefab, transform.position, Quaternion.identity);

        }

    }

}

프리팹 폴더 내의 Character 1을 끌어다가 인스텍퍼 뷰의 매개변수에 대입해야 함.

참고) 변수명은 소문자로 시작, 클래스 명은 대문자로 시작

Character 1 프리팹에 Add Component 로

Character Stat, CharacterBehavior 스크립트 추가

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class CharacterBehavior : MonoBehaviour {

    CharacterStat characterStat;

    // Use this for initialization

    void Start() {

        characterStat = gameObject.GetComponent<CharacterStat>();

    }

    // Update is called once per frame

    void Update() {

    }

    private void OnMouseDown() {

        if (characterStat.canLevelUp()) {

            characterStat.increaseLevel();

        }

    }

}

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class CharacterStat : MonoBehaviour {

    public int level = 1; // 캐릭터의 레벨

    public int hp = 30; // 현재 체력 값

    public int maxHp = 30; // 최대 체력

    public int damage = 5; // 캐릭터의 공격력

    // 레벨 업이 가능한지 여부를 반환합니다.

    public bool canLevelUp() {

        if (level < 3) {

            return true;

        } else {

            return false;

        }

    }

    //  실제로 레벨 업을 수행하는 함수입니다.

    public void increaseLevel() {

        if (level == 1) {

            level = 2;

            maxHp += 25;

            hp = maxHp;

            damage += 5;

            transform.localScale += new Vector3(0.01f, 0.01f, 0);

        } else if (level == 2) {

            level = 3;

            maxHp += 50;

            hp = maxHp;

            damage += 5;

            transform.localScale += new Vector3(0.01f, 0.01f, 0);

        }

    }

    // Use this for initialization

    void Start() {

    }

    // Update is called once per frame

    void Update() {

    }

}

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class CreateCharacter : MonoBehaviour {

    public GameObject characterPrefab;

    private GameObject character;

    // Use this for initialization

    void Start() {

    }

    // Update is called once per frame

    void Update() {

    }

    private void OnMouseDown() {

        if (character == null) {

            character = (GameObject)Instantiate(characterPrefab, transform.position, Quaternion.identity);

        }

    }

}

맵의 특정 지점을 클릭하면 당근 캐릭터 생성됨.

당근 캐릭터 프리팹에도 박스 콜라이더 2D 추가해야 함. (터치감지)

[HTML] 문서변환 참고사이트

[HTML] 문서변환 참고사이트

Office To HTML

docx, pptx 등을 HTML 에서 보여줌. 그런데 doc, ppt 확장자는 지원하지 않음.

https://officetohtml.js.org/index.html

아래는 마이크로소프트에서 제공.

파라미터로 문서 URL을 넘기면 HTML 로 변환해서 보여주는 듯.

https://view.officeapps.live.com/op/embed.aspx?src=http%3A%2F%2Fieee802%2Eorg%3A80%2Fsecmail%2FdocIZSEwEqHFr%2Edoc

일반적으로 다운로드되는 파일이 아니며, 컴퓨터를 손상시킬 수 있습니다.

일반적으로 다운로드되는 파일이 아니며, 컴퓨터를 손상시킬 수 있습니다.


다운로드 컨펌 창에서 [실행] 클릭 시 “일반적으로 다운로드되는 파일이 아니며, 컴퓨터를 손상시킬 수 있습니다.” 경고 메시지가 표시되는 문제.
또는, 파일을 다운로드받아서 실행 시 “Microsoft Defender SmartScreen에서 인식할 수 없는 앱의 시작을 차단했습니다. 이 앱을 실행하면 PC가 위험에 노출될 수 있습니다.” 경고 메세지가 표시되는 문제.

<해결 방법>

우선 인증서가 정상인지 확인해야 한다.

인증서가 정상인데도 경고 메시지가 표시된다면, IE8부터 추가된 스마트스크린 필터 기능 때문이다.

스마크스크린 필터는 악성 프로그램인지 판단하는 프로그램으로, 데이터베이스에 ‘평판’을 기록해서 판단한다.
이때 오래된 인증서는 ‘평판’이 높아서 문제가 없지만, 새로운 인증서일 경우 새로운 프로그램으로 인지해서 ‘평판’이 낮으므로 프로그램이 차단된다고 한다.

결론은 일정 수준으로 ‘평판’이 쌓일 때까지는 경고 메시지를 무시하거나, 스마트스크린 기능을 off해서 사용하면 된다.

cf) 평판이 회복되는 데는 대략 3개월 정도 걸린다고 한다.

 

[IE11] window.open 시 메모리 관리 (about:blank)

[IE11] window.open 시 메모리 관리 (about:blank)

Windows 10 / IE 11 환경에서 메모리 문제로 IE 가 죽는 현상을 개선.

회사에서 MS의 가이드를 받은 내용을 나름대로 정리했다.

우선 IE11의 경우 메모리 1기가 이상이면 죽는다고 보면 된다고 한다.

실제로는 2기가까지도 버티지만, 이론적으로 1기가 미만으로 관리해줘야 한다는 얘기다.

IE11에서 window.open 을 사용할 때 동일한 도메인의 페이지를 여는 경우 하나의 프로세스 안에서 스레드를 늘리게 된다.

이렇게 되면 하나의 프로세스에 대한 메모리가 계속 높아진다.

그런데 window.open 으로 동일하지 않은 새로운 도메인 주소를 열게 되면 새로운 프로세스를 하나 더 늘리게 된다.

따라서 window.open 을 사용할 때 우선 빈 페이지를 열고, 이후 주소값을 바꿔치는 방법을 사용하면, window.open 때마다 프로세스를 늘려 메모리 부하를 분산시킬 수 있다.

<예제>

IE11 에서window.open 으로 특정 페이지를 열면 프로세스가 추가되지 않는다(아래 test1.htm 코드 참고).

반면 IE11 에서 window.open(“about:blank”); 로 빈페이지를 열면 프로세스가 추가된다(아래 test2.htm 코드 참고).

테스트 시 C 드라이브의 파일을 직접 열어서 테스트하지 말고 반드시 웹서버나 WAS를 띄워야 정상적으로 테스트 된다(ex : C:\test\test1.htm 파일을 열어서 하면 안되고, http://localhost/test1.htm 로 테스트 해야 함).

결론은 메모리 문제로 IE 가 죽는 현상을 개선하기 위해서는 window.open 시 window.open(“about:blank”); 로 빈페이지를 열고, 이후 location.href 값을 원하는 페이지 주소로 변경해주는 방식이 유리하다.

1. 일반적인 window.open 방식

test1.htm

<html>
<head>
<title>TEST 1</title>
<script>
    function openWindow() {
        window.open(“test1.htm”);
    }
</script>
</head>
<body>
 <input type=”button” onclick=”openWindow()” value=”OPEN” />
</body>
</html>

 

▲ IE 탭이 1개일 때 iexplore.exe 프로세스 개수 2개

 

▲ IE 탭이 3개일 때 iexplore.exe 프로세스 개수 2개. window.open 했을 때 프로세스 개수가 그대로 유지된다.

2. about:blank 를 사용하는 window.open 방식

test2.htm

<html>
<head>
<title>TEST 2</title>
<script>
    function openWindow() {
        var winObj = window.open(“about:blank”);
        winObj.location.href = “test2.htm”;
    }
</script>
</head>
<body>
 <input type=”button” onclick=”openWindow()” value=”OPEN” />
</body>
</html>



 

▲ IE 탭이 1개일 때 iexplore.exe 프로세스 개수 2개

 

▲ IE 탭이 3개일 때 iexplore.exe 프로세스 개수 4개. window.open 에 따라 프로세스 개수가 늘어났음을 확인할 수 있다.

참고로 크롬과 Edge 의 경우는 현상이 재현되지 않는다. 크롬의 경우 최초 크롬을 하나 띄웠을 때 chrome.exe 프로세스 개수가 7~8개 정도이고, window.open 을 어떤 방식으로 사용하든 프로세스 개수가 유지된다. (Chrome 버전 83.0.4103.97)

Microsoft Edge의 경우도 마찬가지로 최초 Edge를 하나 띄웠을 때 msedge.exe 프로세스 개수가 7~8개 정도이고, window.open 을 어떤 방식으로 사용하든 프로세스 개수가 유지된다. (Miscrosoft Edge 버전 83.0.478.45)

[Unity] Unity 기초강의 내용 정리 (5-1강 ~ 5-2강)

[Unity] Unity 기초강의 내용 정리 (5-1강 ~ 5-2강)

educast 나동빈 님의 <Mobile Defence Game 제작으로 배우는 Unity 기초> 강의를 듣고 내용 정리.

———-

Part 5. Mobile Defense Game

5-1. 프로젝트 개요

5-2. 게임 배경 구현 및 한글 폰트 적용하기

해상도 : 1920 x 1200 (16:10 비율임)

스마트폰 일반적인 비율 : 16:10, 16:9 비율

tile_1을 씬뷰에 넣자. 왼쪽 위 가장자리에 넣자.

tite 1과 tile 2를 번갈아 넣자.

단축키 v를 활용하여

버튼 만들기

Canvas 가 같이 생성된다.

렌더 모드를 카메라로 바꾸고, 렌더 카메라를 메인 카메라로 지정하자.

캔버스의 Plane Distance 는 10으로 지정하자. (그래야 거리가 딱 맞다. 3D 모드에서 확인 가능)

나무판자 모양 버튼 추가하기

이름 : Menu Button

Pos X : 0

Pos Y : 480

Width : 300

Height : 160

폰트 사이즈 : 80

내용 : MENU

폰트 : 나눔바른고딕볼드

캔버스 클릭한 이후 인스펙터 뷰에서 Sorting Layer 클릭

+ 버튼을 이용해 레이어를 여러개 만들자.

1. Background : 배경 및 타워를 설치할 공간

2. Unit : 몬스터, 캐릭터 등 포함

3. Additional : 캐릭터 타워가 발사한 총알, 캐릭터 HP 체력바

4. Basic Canvas : 게임 스코어, 목숨 등 UI

5. Menu Canvas : 메뉴 화면을 눌렀을 때 화면을 덮게될 레이어

Canvas 의 레이어를 Basic Canvas 로 바꾸자.

Background Tile 을 모두 선택해서 Sorting Layer를 Background 로 지정하자

캔버스 안에 이미지를 만든다. (Round Image)

Source Image 는 Wood_2

Pos X : -740

Pos Y : 480

Width : 360

Height : 160

이미지 안에 자식요소로 Text 를 생성한다.

폰트 크기 : 72

텍스트 : ROUND 1

Horizontal Overflow : Overflow

Vertical Overflow : Overflow

Alignment : 가로세로 모두 가운데 정렬

폰트 : 나눔바른고딕볼드

Round Image 를 복제해서 Life Image를 만들자.

Pos X : -360

내부에 이미지를 만든다. 당근 모양 이미지를 Source Image 로 지정한다.

당근을 좌측으로 옮기고, 텍스트를 우측으로 옮긴다.

텍스트 내용은 : 10 이라고 쓴다.

(당근모양) : 10 이 되도록.

Round Image 를 복제해서 Seed Image를 만들자.

Pos X : 640

Pos Y : 480 (그대로)

width : 480

height : 160 (그대로)

텍스트 내용 : 씨앗 : 10000

좌측 하이어라키 뷰에서 텍스트 객체만 Ctrl 키 누른채로 선택해서, 글자 색상을 흰색으로 바꾸고 outline을 주자.

outline 컴포넌트를 추가하면 된다. 값은 x 3, y 3 으로 지정하기.

Sprites 폴더 안의 fense 라는 이미지를 씬뷰에 드래그앤드랍으로 넣는다.

Position x -7 y -1 z 0

Rotation x 0 y 0 z 90

Sacle x 0.6 y 0.3 z 1

Layer 는 Unit으로 설정. 충돌처리를 위함.

[Unity] Unity 기초강의 내용 정리 (4-1강 ~ 4-5강)

[Unity] Unity 기초강의 내용 정리 (4-1강 ~ 4-5강)

educast 나동빈 님의 <Mobile Defence Game 제작으로 배우는 Unity 기초> 강의를 듣고 내용 정리.

———-

Part 4. Animation & Audio

4-1. Animation System

애니메이션 시스템에 대한 전반적인 구조를 이해해보자.

Object

=> Animator Component 추가

=> Animator Controller 추가

=> Animator Controller 는 n 개의 Animation Clip (State) 으로 구성됨

=> Animation Clip 은 n개의 Sprite 로 구성됨

(여러 개의 스프트라이트 이미지를 준비해야 함)

상단메뉴 [Window] – [Animation] – [Animation] 클릭해서 탭 추가

상단메뉴 [Window] – [Animation] – [Animator] 클릭해서 탭 추가

1. 몬스터1 오브젝트에 Animator 컴포넌트를 추가한다.

2. 우측 인스펙터 뷰를 보면 Controller 항목이 비어있다.

3. Monster_1.controller 를 Controller 항목에 드래그앤드랍한다.

4. 게임 재생을 해본다.

———-

4-2. Animation Clip – Sprite Change

1. 몬스터1 을 드래그앤드랍으로 씬에 넣자.

2. Scale X 0.4, Y 0.4

Position X 0, Y 0

3. 상단메뉴 [Window] – [Animation] – [Animation] 클릭해서 탭 추가

상단메뉴 [Window] – [Animation] – [Animator] 클릭해서 탭 추가

4. 애니메이션을 넣고자 하는 오브젝트 클릭

5. Animation 탭의 [create] 버튼 클릭

anim 파일 저장 : Monster_1_Walking 이라는 이름으로 저장

프로젝트 폴더 내의 Assets\Animations 폴더 안에 저장을 권장

ex) C:\unity_workspaces\Practice4-1\Assets\Animations

애니메이터 컴포넌트가 추가된 것을 볼 수 있음.

6. Animation 탭에서 녹화 버튼 클릭

7. Animation 탭에서 0.1 초 부분을 클릭하여 하안색 선이 생기면,

몬스터 첫번째 그림을 드래그앤드랍하기.

8. 0.1초에 몬스터 첫번째 그림 넣기

0.2초에 몬스터 두번째 그림 넣기

0.3초에 몬스터 세번째 그림 넣기

0.4초에 몬스터 네번째 그림 넣기

이 때 애니메이션이 어색하지 않으려면,

애니메이션의 끝과 처음을 동일하게 해줘야 한다.

0.4 초 부분 그림을 복사해서(Ctrl + c) 0초 부분에 붙여넣는다(Ctrl + v).

9. 속도를 조절하려면 Samples 값을 60 에서 30 으로 조절

10. 이동 애니메이션을 하고 싶다면

0.4초를 클릭하고 몬스터를 이동시킨다.

그러면 이동 자체가 애니메이션 화 된다.

그런데 이동을 애니메이션에 포함하는 방법은 권장되는 방법은 아니다. 참고삼아 알고만 있자.

———-

4-3. Skeletal Animation (New Feature in 2018.2)

골격(뼈) 기반의 애니메이션

애니메이션의 한 장면 장면을 그려야 하는 어려움을 줄여줌.

[Window] – [Package Manager] – [All] 탭 클릭

– [2D Animation] 패키지 클릭하고 우측의 [Install] 클릭

설치가 다 되면 프로젝트 뷰 Sprites 폴더에 Flying Dragon 이미지를 드래그앤드랍해서 추가한다.

1. 프로젝트 뷰에서 이미지를 클릭한다.

2. 인스펙터 뷰에서 Sprite Editor 라는 버튼 클릭.

3. Sprite Editor 창이 뜨면 좌측 상단의 [Sprite Editor] 클릭하고

[Bone Editor]로 바꾼다.

아래쪽에 Tools 창이 작게 뜬다.

뼈가 1개 있는 아이콘 : Create Bone

뼈가 2개 있는 아이콘 : Create Free Bone

4. Create Bone 을 눌러서 가장 중심이 되는 뼈를 만든다. 처음 만드는 뼈가 Root 뼈(Root Bone)다.

5. 다시 Create Bone 을 눌러서 다음뼈를 만들자.

입까지 뼈를 이어서 생성하자.

6. Create Free Bone 로 루트 뼈 좌우측에 뼈를 하나씩 더 만든다. (좌측 한 개, 우측 한 개)

다시 Create Bone 을 눌러서 날개를 잇는 뼈를 계속 추가한다.

7. 다시 Create Free Bone 로 루트 뼈 아래에 뼈를 하나 더 만든다.

다시 Create Bone 을 눌러서 꼬리를 잇는 뼈를 계속 추가한다.

8. 우측상단의 Apply 버튼 눌러서 적용

9. Sprite Editor 창 좌측 상단의 [Bone Editor] 클릭하고

[Skin Weights and Geometry Editor]로 바꾼다.

10. 우측의 [Generate] 버튼 클릭

Outline Detail 0.3

Alpha Tolerance 33

Subdivide 50

Outline Detail : 아웃라인을 얼마나 꼼꼼하게 그릴지

Alpha Tolerance : 수치가 높을수록 가장자리 푸른 선이 이미지에 가깝게 생성

Subdivide : 좀 더 세부적으로 안에 점을 생성

11. 우측의 [Weights] 버튼 클릭해서 확인

[Auto] 버튼 클릭

색이 입혀져야 한다.

[Apply] 버튼 클릭

1. 씬 뷰에 플라잉 드래곤을 드래그앤드랍해서 넣는다.

2. [Add Component] 로 [Sprite Skin]을 추가한다.

[Sprite Skin]을 추가해야 뼈대가 보인다.

3. [Create Bones] 버튼 클릭

4. 상단메뉴 [Window] – [Animation] – [Animation] 클릭해서 탭 추가

상단메뉴 [Window] – [Animation] – [Animator] 클릭해서 탭 추가

5. Animation 탭의 [create] 버튼 클릭

anim 파일 저장 : Skeletal Animation 이라는 이름으로 저장

프로젝트 폴더 내의 Assets\Animations 폴더 안에 저장을 권장

ex) C:\unity_workspaces\Practice4-1\Assets\Animations

6. 녹화 버튼을 누르고

0.1 초에 용 머리를 내리고

0.2 초에 용 머리를 높이고

0.3 초에 다시 용머리를 내린다.

Samples 를 20 으로 낮춘다.

날개를 펄럭이는 용을 만들어보자.

———

4-4. Animator Controller

1. 프로젝트 뷰에서 이미지를 클릭한다. (피망 모양 몬스터)

2. 인스펙터 뷰에서 Sprite Editor 라는 버튼 클릭.

3. Sprite Editor 창이 뜨면 좌측 상단의 [Sprite Editor] 클릭하고

[Bone Editor]로 바꾼다.

4. 좌측에 Root Bone 을 만든다. (피망 머리 뒤쪽에 2개)

Free Bone 을 만든다.

5. 우상단에 Apply 버튼 누른다.

6. Sprite Editor 창 좌측 상단의 [Bone Editor] 클릭하고

[Skin Weights and Geometry Editor]로 바꾼다.

7. 우측의 [Generate] 버튼 클릭

Outline Detail 0.3

Alpha Tolerance 33

Subdivide 50

8. 우측의 [Weights] 버튼 클릭해서 확인

[Auto] 버튼 클릭

색이 입혀져야 한다.

[Apply] 버튼 클릭

1. 씬 뷰에 피망 캐릭터를 드래그앤드랍해서 넣는다.

Scale 값을 0.5, 0.5 로 줄인다.

2. [Add Component] 로 [Sprite Skin]을 추가한다.

[Sprite Skin]을 추가해야 뼈대가 보인다.

3. [Create Bones] 버튼 클릭

4. Animation 탭의 [create] 버튼 클릭

anim 파일 저장 : Character Idle 이라는 이름으로 저장

프로젝트 폴더 내의 Assets\Animations 폴더 안에 저장을 권장

ex) C:\unity_workspaces\Practice4-4\Assets\Animations

애니메이터 컴포넌트가 추가된 것을 볼 수 있음.

cf) Idle 은 빈둥거리다 라는 뜻.

캐릭터가 건들건들 대기하는 애니메이션을 만들기 위함.

5. 1.0 초부분에 뒤통수 위쪽과 입 아래쪽을 구부려준다.

Samples 값을 20 으로 줄여서 느리게 만든다.

1. Animator 탭

Character Idle 을 선택해보자.

인스펙터 뷰를 보자.

Speed 1

이 값을 높이면 속도가 빨라진다.

다른 항목들은 크게 중요하지 않음.

2. Animator 탭

Character Idle 스테이트를 더블클릭하게 되면 그 스테이트에 적용된 애니메이션 클립 파일을 선택하게 됨.

(또는 프로젝트 뷰에서 Character Idle 을 더블클릭하자)

여기서 다른건 중요하지 않고 Loop Time을 보자. Loop Time 체크 해제 시 한 번만 재생하고 종료.

3. 씬 뷰의 피망 모양 캐릭터 선택하고 Animation 탭

[Preview] 버튼 밑의 [Character Idle] 클릭하면 [Create New Clip…] 이라는 버튼 보일 것임.

Character Die.anim 라는 이름으로 저장

4. 죽는 애니메이션

쓰러지면서 반투명 처리, 회색빛으로 바꾸면 됨.

녹화 버튼을 누른채로 작업하자.

1.0초 에서 넘어지도록 + 회색빛 + 반투명 처리

2.0초 에서 완전투명처리

5. Character Idle 우클릭해서 Make Transition 누른 후

Character Die 에 연결하기

Transition Duration : 매끄럽게 연결

Transitin Offset

2D 게임에서는 일반적으로

Transition Duration 값 0, Transitin Offset 값 0으로 지정하면 된다.

Exit Time 1은 애니메이션 모두 실행 후 넘어가는 것.

Exit Time 0.5이면 애니메이션 절반만 실행 후 다음으로 넘어간다.

6. 프로젝트 뷰에서 Character Die를 더블클릭하자.

Loop Time 체크해제하자. Loop Time 체크 해제 시 한 번만 재생하고 종료.

7. 이제 게임 재생했을 때 피망 캐릭터가 한 번 idle 애니메이션을 실행하고

바로 죽는 애니메이션을 실행하게 됨.

8. [Animator] 탭의 좌측 [Parameters] 탭

우측의 [+] 버튼 클릭하고 trigger 선택.

[die_trigger]라는 이름으로 생성

9. [Animator] 탭에서 [Base Layer] 화면속 트랜지션(화살표)를 선택

우측 인스펙터 뷰의 [Conditions] 에서 [+] 버튼을 클릭

[die_trigger] 를 추가하기

10. 이제 게임 재생했을 때 피망 캐릭터가 계속 idle 애니메이션을 실행함.

[Animator] 탭의 좌측 [Parameters] 탭에서

[die_trigger] 라디오 버튼을 체크하게 되면 죽는 애니메이션을 실행하게 됨.

———-

4-5. Prameter와 Script

1. 캐릭터 클릭

2. [Animator] 탭의 좌측 [Parameters] 탭

우측의 [+] 버튼 클릭하고 float 선택.

[hp_float]라는 이름으로 생성

3. [Animator] 탭에서 [Base Layer] 화면속 트랜지션(화살표)를 선택

우측 인스펙터 뷰의 [Conditions] 에서 [+] 버튼을 클릭

[hp_float] 를 추가하기

콤보박스 [Less] 선택

값은 1

(1 미만일 경우 Die 애니메이션 실행하기 위함)

4. 프로젝트 뷰에 Scripts 폴더 만들고

그 안에 Character_2_Animation 라는 이름의 C# 스크립트 생성

자료형 : 어떤 종류의 값을 담을지 결정하는 통의 모양

변수 : 내가 사용하기 위해 만든 실제 통

값 : 그 실제 통에 담은 내용물

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class Character_2_Animation : MonoBehaviour {

    Animator character_2_animator = null;

    float character_2_hp = 0.0f;

   

 void Start () {

        character_2_animator = gameObject.GetComponent<Animator>(); 

 }

 

 void Update () {

  if (Input.GetKeyDown(KeyCode.Space)) {

            character_2_hp = character_2_animator.GetFloat(“hp_float”);

            character_2_animator.SetFloat(“hp_float”, character_2_hp – 10);

        }

        if (Input.GetKeyDown(KeyCode.A)) {

            character_2_animator.SetBool(“again_bool”, true);

        }

        if (Input.GetKeyDown(KeyCode.S)) {

            character_2_animator.SetBool(“again_bool”, false);

        }

    }

}

———-

4-6. Audio

Source 와 Listener 의 상호작용

메인 카메라는 이미 오디오 리스너 컴포넌트가 적용되어 있다.

1. 캐릭터 선택하고, Add Component 로 [Audio Source] 추가

2. AudioClip 을 선택해준다. (monster_1.mp3)

재생하면 곧바로 몬스터 소리가 날 것이다.

이유는 Play On Awake 가 체크되어 있기 때문이다.

그 아래 Loop 까지 체크해주면 반복적으로 동일한 소리가 반복 재생된다.

배경음은 보통 빈 오브젝트를 만들고 배경음악을 넣는다.

1. Empty Object를 만든다.

2. 이름을 BGM Manager 로 변경

3. Add Component 로 [Audio Source] 추가

4. AudioClip 을 선택해준다. (background.mp3)

5. Play On Awake 와 Loop 를 체크한다.

* 버튼으로 음악 실행하기

버튼을 만들고, On Click() 부분에

좌측 항목은 Character_2 를 선택하고,

우측 항목은 AudioSource -> Play() 선택

* 사운드 압축

Audios 폴더 안에서 background 선택

우측의 인스펙터 뷰에서 Force To Mono 체크 : 오디오 사운드를 좌우 구분 없는 모노 사운드로 변경. 용량 감소됨.

Normalize까지 체크하자.

배경음악의 Load Type은 반드시 Decompress On Load 를 사용하지 말자. 메모리 많이 차지하고 렉을 유발함.

Compressed In Memory 로 선택하자.

[Tomcat] Unable to load server configuration from [C:\…\server.xml]

[Tomcat] Unable to load server configuration from [C:\…\server.xml]

Tomcat7 에서 아래 오류 메시지 발생한 경우

Unable to load server configuration from C:\…\server.xml]

ex) Unable to load server configuration from

[C:\bbmon\workspaces\test\project\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\conf\server.xml]

해당 위치(catalina.base 위치)에 server.xml 이 존재하는지 먼저 확인하자.

cf) VM arguments 중 catalina.base 값에 따라 server.xml 경로가 결정되는 것으로 보임.

-Dcatalina.base=”C:\bbmon\workspaces\test\project\.metadata\.plugins\org.eclipse.wst.server.core\tmp0″

[iOS] 아이폰 버튼(UIButton) 밑줄 나오는 현상

[iOS] 아이폰 버튼(UIButton) 밑줄 나오는 현상

개발한 아이폰 앱 내의 버튼(UIButton) 텍스트에 밑줄(underline)이 나오는 현상.

처음엔 버그인줄 알고, Xcode 에서 애꿎은 xib 파일만 계속 고쳤다.

문제는 앱 개발 단 문제가 아니었고, 아이폰 설정 문제였다.

아이폰 바탕화면에서 [설정] 아이콘 – [손쉬운 사용] – [디스플레이 및 텍스트 크기] – [버튼 모양] 항목을 off 처리하면 된다.

이제 앱에서 보이는 버튼 밑줄이 사라질 것이다.

<상세설명>

1. 아이폰 바탕화면에서 [설정] 아이콘 클릭

 

2. [손쉬운 사용] 클릭

3. [디스플레이 및 텍스트 크기] 클릭


4. [버튼 모양] 항목을 off 처리


참고사이트 : https://apple.stackexchange.com/questions/124277/some-texts-automatically-underlined-in-iphone-5-with-ios-7-1

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

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

지난 이야기 : iOS 앱 관련 인증서 정리 첫번째(https://blog.naver.com/bb_/221655632798)

iOS 앱 개발을 하면서 각기 다른 기관의 인증서를 열 번 가까이 만들어본 것 같은데, 아직도 애플 인증서가 어렵고 헷갈린다.

미래에 또다시 고통받고 있을 나를 위해 이 글을 적는다.

1. 엔터프라이즈 계정

애플 계정은 크게 개발자 계정과 엔터프라이즈 계정으로 나뉜다.

개발자 계정은 앱을 개발해서 앱스토어(App Store)에 올릴 수 있다. n명이 사용해야 하는 일반적인 앱의 경우 개발자 계정이면 충분하다.

인하우스(InHouse) 방식으로는 배포가 불가능하다.

반면 엔터프라이즈 계정은 앱을 개발해서 인하우스(InHouse) 방식으로 배포할 수 있다.

인하우스 방식은 특정한 기업 내 구성원들이 앱을 사용하기 위한 방식이다.

엔터프라이즈 계정은 구성원이 200명 이상인 기업에서 생성할 수 있다.

엔터프라이즈 계정을 신청해서 인하우스 인증서를 발급하기까지, 소요기간은 최초 신청일로부터 6주 정도가 걸린다.

신청해서 엔터프라이즈 계정이 생성되기까지 4주 정도 걸린다.

엔터프라이즈 계정이 활성화되고(Activated) 2주를 더 기다리면 인하우스 인증서를 발급 가능한 상태가 된다.

2. CSR

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

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

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

(1) [키체인] 실행 – 상단메뉴의 [키체인 접근] – [인증서 지원] – [인증 기관에서 인증서 요청…] 으로 CSR을 생성 가능하다.

(2) 이 때 사용자 이메일 주소, 일반 이름(기관명)을 적게 되는데, 정확히 적어야 하는 것 같다. 애플 엔터프라이즈 계정의 경우 해당하는 이메일, 기관명을 정확히 기입하자. (대소문자 및 띄어쓰기 주의)

(3) [디스크에 저장됨] 을 체크하고 적당한 위치에 저장할 것.

한 번 만들어놓은 CSR은 계속 재사용이 가능하다. 이메일과 기관명이 동일하다면(동일한 엔터프라이즈 계정 내에서 인증서 발급이 필요하다면) 한 번 만들어놓은 CSR을 계속 사용하면 된다.


3. Devices

디바이스란 아이폰/아이패드라고 생각하면 된다. 애플 개발자 사이트에 Devices를 추가하기 위해서는 특정 아이폰/아이패드의 udid를 알아내야 한다. 사파리 브라우저에서 udid.io 에 접속하여 udid 값을 가져올 수 있다.

4. Certificates, Identifiers

Certificates는 인증서이고, 프로비저닝 프로파일(Provisioning Profiles)을 만들기 위해 필요하다.

개발용 인증서는 Development 라는 글자가 포함된 인증서(Apple Development, iOS App Development)를 만들면 된다.

배포용 인증서는 Distribution 또는 InHouse(Enterprise 계정용) 라는 글자가 포함된 인증서를 만들면 된다.

5. Provisioning Profiles

항목 3에서 만든 인증서를 이용해서 나중에 프로비저닝 프로파일을 만들게 되는데, 여러 개의 인증서를 포함할 수 있다. 예를 들어서 개발용 프로비저닝 프로파일을 만든다면, Apple Development 와 iOS App Development 두 개 항목을 체크해서 생성할 수 있다.

6. 기타 문제해결

인증서를 모두 정상적으로 생성했는데 Xcode 에서 Sigining 관련 오류가 난다면, 관련 인증서를 모두 다운받고 더블클릭해서 키체인에 등록한다. 그래도 안되면 Automatically manage signing 항목을 체크해본다.


만약 No signing certificate “iOS Distribution” found 오류가 계속 없어지지 않는다면 다음 포스트를 참고하자.

(https://blog.naver.com/bb_/222155599575)