지식메모

반응형

 

 

버튼 속성에

 

android:background="#00ff0000"  

를 추가해준다.

 

 


<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00ff0000" //투명
android:id="@+id/button" />

 

 

반응형
반응형

 

이런 Json배열 하나에 여러개의 Json이 있다고 가정했을 때

 

Json 값마다 있는 idx,link,description 키값을 사용해 데이터를 저장해보았다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
String eventArray = null//Json Array를 String으로 저장하기 위한 변수 선언
try {
    eventArray = loadHtml(); //loadHtml()는 Json Array를 파싱해서 String으로 가져오는 함수라 가정
catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("결과물 : " + eventArray);
try {
    JSONArray array = new JSONArray(eventArray); //JSONArray형식으로 파싱하기 위해 새로 선언해주며 eventArray를 집어 넣어준다.
    list_cnt = array.length(); //Json 배열 내 JSON 데이터 개수를 가져옴
 
    //key의 value를 가져와 저장하기 위한 배열을 생성한다
    getDescription = new String[list_cnt]; //decription 저장용
    getLink = new String[list_cnt]; //link 저장용
    getImageUrl = new String[list_cnt]; //imageUrl 저장용
 
    for (int i = 0; i < list_cnt; i++) { //JSONArray 내 json 개수만큼 for문 동작
 
        JSONObject jsonObject = array.getJSONObject(i); //i번째 Json데이터를 가져옴
        getDescription[i] = jsonObject.getString("description");  //descripton 값을 배열에 저장
        getLink[i] = jsonObject.getString("link");  //link 값을 배열에 저장
        getImageUrl[i] = jsonObject.getString("imageUrl");  //imageurl 값을 배열에 저장
        Log.i("JSON Object", jsonObject + "");
        Log.i("JsonParsing", getDescription[i] + "," + getLink[i] + "," + getImageUrl[i]);
 
    }
cs

 

Log로 찍어보면

JsonObject의 로그

I/JSON Object: {"idx":15858,"imageUrl":"http:\/\/img.cgv.co.kr\/Event\/Event\/2017\/0330\/some_240x200_01.jpg","link":".\/detail-view.aspx?idx=15858&menu=2","description":"<어느날>1+1 예매 이벤트"}

 

파싱해낸 데이터 description, link, imageUrl 순으로 파싱이 된 걸 볼 수 있다.

I/JsonParsing: <어느날>1+1 예매 이벤트,./detail-view.aspx?idx=15858&menu=2,http://img.cgv.co.kr/Event/Event/2017/0330/some_240x200_01.jpg

 

배열에 잘 들어갔는지 description을 예로 확인을 해보면

for(int i=0;i<description.length;i++){
    System.out.println("배열값 : "+description[i]);
}

 

I/System.out: 배열값 : <시간위의 집>스타★라이브톡

I/System.out: 배열값 : <보통사람>1+1 예매권 2차 이벤트

I/System.out: 배열값 : <로즈> 기대평 이벤트

I/System.out: 배열값 : <공각기동대:고스트 인 더 쉘>예매 경품 이벤트

 

description 값만 따로 배열에 잘 들어가있음을 확인할 수 있다.

 

이제 파싱해낸 값으로 다양한 곳에 쓸 수 있다.

 

 

 

반응형
반응형

subString 함수사용

subString(시작위치,끝위치);

예를 들어 애국가에서 

하느님이 ~ 우리나라만세 사이에 텍스트를 추출하고 싶다면

String song = "하느님이 보우하사 우리나라만세";

int start = song.indexOf("하느님이"); //하느님이 위치 추출

int end = song.indexOf("우리나라만세"); //우리나라만세 추출

String parseText = song.subString(start,end);

이런 방법도 있다



반응형
반응형




Naver Open API가 방식이 바뀌어서 이전 글에서 했던 방식은 동작이 되지않는다.

하지만 사전 준비는 동일하니 아래 글을 참고하면 된다.


REST-Assured 를 사용하여 maven 프로젝트로 간단한 API 테스트

http://yonoo88.tistory.com/886


우선 네이버오픈API 페이지에 들어간다.

https://developers.naver.com/main/

들어가서 상단 Application > 애플리케이션 등록을 들어간다.


애플리케이션 이름적고 사용할 API 추가하고 환경은 저렇게 해주어도 된다.



등록하기를 누르면 아래와 같은 화면이 나오는데 

Client ID 와 Client Secret 저 2개가 필요하다.

Client Secret는 우측 보기를 누르면 표시된다.

이 2개를 복사해서 코드에 넣어주어야 한다.

저 2개 값을 header 값으로 추가해주어야 한다.

String 값으로 Client ID,와 Client Secret 에 개인 값을 넣어준다.

그럼 api가 정상적으로 동작한다.



예제코드 뉴스 검색 API 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package NaverAPITest.NAPITest;
 
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
 
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
 
import org.junit.Before;
import org.junit.Test;
 
public class test {
 
    @Before
    public void apiTest() {
         baseURI = "http://openapi.naver.com";
//        baseURI = "http://maps.googleapis.com";
        port = 80;
    }
 
    @Test
    public void test() {
        try {
            String text = URLEncoder.encode("최순실""UTF-8");
            given().
              header("X-Naver-Client-Id""Client ID").
              header("X-Naver-Client-Secret","Client Secret").
             params( //URL 주소의 파라미터 값을 넣어줌
             "query",text     
             ).when().
            
             /*baseURL 을 제외한 뒷부분 주소를 get하고
             *body에서 해당 title 내용을 가져올 위치 지정 후
             *확인할 값을 equalTo에 넣어 확인*/
             get("/v1/search/news.xml").then().body("rss.channel.title", equalTo("Naver Open API - news ::'최순실'"));
//             get("/v1/search/news.xml").then().toString();
            
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
 
}
 
cs



반응형
반응형



또 이상한 에러가 발생.. 이클립스가 이상한건가..

에러 내용은 이러하다

java.lang.SecurityException: class "org.hamcrest.Matchers"'s signer information does not match signer information of other classes in the same package

at java.lang.ClassLoader.checkCerts(ClassLoader.java:895)

at java.lang.ClassLoader.preDefineClass(ClassLoader.java:665)

at java.lang.ClassLoader.defineClass(ClassLoader.java:758)

at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)

at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)

at java.net.URLClassLoader.access$100(URLClassLoader.java:73)

at java.net.URLClassLoader$1.run(URLClassLoader.java:367)

at java.net.URLClassLoader$1.run(URLClassLoader.java:361)

at java.security.AccessController.doPrivileged(Native Method)

at java.net.URLClassLoader.findClass(URLClassLoader.java:360)

at java.lang.ClassLoader.loadClass(ClassLoader.java:424)

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)

at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

at NaverAPITest.NAPITest.test.test(test.java:35)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:483)

at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)

at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)

at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)

at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)

at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)

at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)

at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)

at org.junit.runners.ParentRunner.run(ParentRunner.java:363)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)

at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)



찾아보니 라이브러리 중복으로 인해 발생된다고 한다.

Junit의 라이브러리가 문제라는데..

org.hamcrest.Matchers

아무튼 JUnit4 저걸 통째로 remove하여 제거하였다.


그리고 다시 실행했더니 웬 에러???

pom.xml을 가봤더니 Junit 3.8.1 로 되어있었다. 


그래서 저걸 지워버리고

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

</dependency>

이걸 추가해주었더니 에러가 사라졌다!

이제 실행하니 잘되었따




반응형
반응형




원인이 뭔지 모르겠다

사내망이라 보안관련 문제인가?

이거저거 찾아보다가 ssh 관련해서 rsa,dsa 파일 만드는 방법이 있길래 적어본다.

github 로그인하면 ssh 키 등록하라고 어쩌구 뜨길래 관련해서 뒤져봤었다.


ssh 어쩌구 뜨는데 저 파일이 컴퓨터에 없었다.

그래서 이클립스에 들어가서 보는 법을 찾아서 따라해보았다.

Windows > preferences > General > NetWork Connection > SSH2 에 가본다.

그럼 아래와 같은 화면이 뜬다.



근데 내 컴퓨터에는 .ssh 폴더가 존재하지 않는다.

일단 오른쪽 하단 Apply를 눌러본다.

그럼 만들겠냐고 창이 뜬다. Yes



그럼 폴더가 만들어진다.





이제 id_dsa. id_rsa 파일이 없으므로 키 생성을 해야한다.

Key Management 탭을 누른다.




Generate DSA Key를 누르면 키가 생성된다. 



우측 하단 Save Private Key를 눌러 파일을 .ssh 폴더에 저장한다.



방금 만든 .ssh 경로에 저장한다.




RSA 키도 마찬가지로 생성한다.


이제 다 마치고 Apply 를 누른 후 OK로 Preferences를 빠져나온다.





반응형
반응형

yum install kvm 또는

yum install virt-manager libvirt libvirt-python phyton-virtinst

실행하니 아래와 같은 에러 발생


Transaction check error:
file /usr/lib/systemd/system/blk-availability.service from install of device-mapper-7:1.02.107-5.el7_2.5.x86_64 conflicts with file from package lvm2-7:2.02.105-14.el7.x86_64
file /usr/sbin/blkdeactivate from install of device-mapper-7:1.02.107-5.el7_2.5.x86_64 conflicts with file from package lvm2-7:2.02.105-14.el7.x86_64
file /usr/share/man/man8/blkdeactivate.8.gz from install of device-mapper-7:1.02.107-5.el7_2.5.x86_64 conflicts with file from package lvm2-7:2.02.105-14.el7.x86_64

Error Summary
이런 에러가 떴다면
 
yum -y update lvm2*

명령어를 실행하여 패치 후 다시 시도하면 해결된다.

반응형
반응형





https://blog.stylingandroid.com/permissions-part-3/

위 블로그 글을 참고 하였고 

프로젝트는 작성자의 깃허브 사이트 

https://github.com/StylingAndroid/Permissions/tree/Part3

를 참고 하였다.


프로젝트를 실행하면 이러한 모습이다



코드를 참고하면서 내 프로젝트에 필요한 것만 참고함.


먼저

PermissionsActivity.java 와 PermissionsChecker.java를 프로젝트에 붙여넣기 하였다.

추가 설명은 코드 내 주석을 달아놓았다.



PermissionsActivity.java

37라인

setContentView(R.layout.new_custom_full);

이 부분은 팝업을 표시할 액티비티의 xml 파일명으로 변경해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 
 
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
 
public class PermissionsActivity extends AppCompatActivity {
    public static final int PERMISSIONS_GRANTED = 0;
    public static final int PERMISSIONS_DENIED = 1;
 
    private static final int PERMISSION_REQUEST_CODE = 0;
    private static final String EXTRA_PERMISSIONS = "com.stylingandroid.permissions.EXTRA_PERMISSIONS";
    private static final String PACKAGE_URL_SCHEME = "package:";
 
    private PermissionsChecker checker;
    private boolean requiresCheck;
 
    public static void startActivityForResult(Activity activity, int requestCode, String... permissions) {
        Intent intent = new Intent(activity, PermissionsActivity.class);
        intent.putExtra(EXTRA_PERMISSIONS, permissions);
        ActivityCompat.startActivityForResult(activity, intent, requestCode, null);
    }
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getIntent() == null || !getIntent().hasExtra(EXTRA_PERMISSIONS)) {
            throw new RuntimeException("This Activity needs to be launched using the static startActivityForResult() method.");
        }
        setContentView(R.layout.new_custom_full); //xml 파일명 변경 필요
 
        checker = new PermissionsChecker(this);
        requiresCheck = true;
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        if (requiresCheck) {
            String[] permissions = getPermissions();
 
            if (checker.lacksPermissions(permissions)) {
                requestPermissions(permissions);
            } else {
                allPermissionsGranted();
            }
        } else {
            requiresCheck = true;
        }
    }
 
    private String[] getPermissions() {
        return getIntent().getStringArrayExtra(EXTRA_PERMISSIONS);
    }
 
    private void requestPermissions(String... permissions) {
        ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
    }
 
    private void allPermissionsGranted() {
        setResult(PERMISSIONS_GRANTED);
        finish();
    }
 
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CODE && hasAllPermissionsGranted(grantResults)) {
            requiresCheck = true;
            allPermissionsGranted();
        } else {
            requiresCheck = false;
            showMissingPermissionDialog();
        }
    }
 
    private boolean hasAllPermissionsGranted(@NonNull int[] grantResults) {
        for (int grantResult : grantResults) {
            if (grantResult == PackageManager.PERMISSION_DENIED) {
                return false;
            }
        }
        return true;
    }
 
    //모두 거부 하였을 때 나타나는 팝업 창 설정입니다.
    private void showMissingPermissionDialog() {
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PermissionsActivity.this);
        dialogBuilder.setTitle(R.string.help); //팝업 창 타이틀
        //팝업 안내 메시지 부분으로 string.xml에서 설정한 메시지를 노출합니다.
        dialogBuilder.setMessage(R.string.string_help_text);
        //팝업 창 버튼 2개를 노출합니다.
        //확인버튼을 누르면 앱 종료?인데 저는 팝업 창만 사라졌네요
        dialogBuilder.setNegativeButton(R.string.quit, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                setResult(PERMISSIONS_DENIED);
                finish();
            }
        });
        //설정 버튼을 누르면 설정 화면으로 이동합니다.
       dialogBuilder.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                startAppSettings();
            }
        });
        dialogBuilder.show();
    }
 
     private void startAppSettings() {
        Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse(PACKAGE_URL_SCHEME + getPackageName()));
        startActivity(intent);
    }
}
cs



PermissionsChecker.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.v4.content.ContextCompat;
 
class PermissionsChecker {
    private final Context context;
 
    public PermissionsChecker(Context context) {
        this.context = context;
    }
 
    public boolean lacksPermissions(String... permissions) {
        for (String permission : permissions) {
            if (lacksPermission(permission)) {
                return true;
            }
        }
        return false;
    }
 
    private boolean lacksPermission(String permission) {
        return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED;
    }
 
}
 
cs


발생하는 에러는 알맞게 처리한 다음

AndroidMenifest.xml 에 PermissionsActivity를 추가해준다.

1
2
3
4
5
6
7
8
9
10
        <!--PermissionsActivity 를 추가해줍니다.-->
        <!--팝업 적용을 위해 @style:AppTheme 추가해줍니다-->
        <activity
            android:name="com.yonoo.my.PermissionsActivity"
            android:configChanges="orientation|screenSize|screenLayout|keyboard|keyboardHidden"
            android:label="@string/app_name"
            android:theme="@style/AppTheme"
            android:screenOrientation="nosensor" >
        </activity>
 
cs


권한도 추가

1
2
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.CAMERA" />
cs



앱 테마 적용을 위해 style.xml에 다음을 추가해준다.

(다국어 설정이 되어있다면 국가별 style.xml에도 추가해준다.)

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<resources>
 
    <!--
        Base application theme, dependent on API level. This theme is replaced
        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
    -->
    <style name="AppBaseTheme" parent="android:Theme.Light">
        <!--
            Theme customizations available in newer API levels can go in
            res/values-vXX/styles.xml, while customizations related to
            backward-compatibility can go here.
        -->
    </style>
    <!--팝업 호출을 위해 아래 스타일이 필요합니다.-->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    </style>
</resources>
 
cs


그리고 권한 팝업 노출시 권한 설명을 위한 메시지를 string.xml에 추가하여준다. 마찬가지로 다국어 string에도 추가

이 메시지는 PermissonsActivity의 showMissingPermissionDialog에서 사용된다.

1
2
3
4
5
6
    <!--권한 팝업에서 노출될 메시지를 설정합니다.-->
    <string name="settings">설정</string>
    <string name="help">Help</string>
    <string name="string_help_text"> 저장소와 카메라 권한이 필요합니다. </string>
    <string name="quit">확인</string>
 
cs


그리고 권한 팝업을 띄울 액티비티파일에 다음 코드를 추가해준다.

ex) NewCustomFullActivity 에 권한 팝업을 띄울거라면 변수를 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class NewCustomFullActivity implements OnClickListener{
 
    //권한 체크를 위한 값 선언
    private static final int REQUEST_CODE = 0;
    /*요청받은 권한을 설정합니다. 여기선 저장소와 카메라를 설정
    * android.Manifest.permission.WRITE_EXTERNAL_STORAGE
    * android.Manifest.permission.CAMERA
    * */
    private static final String[] PERMISSIONS = 
            new String[]{
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    android.Manifest.permission.CAMERA
            };
    //PermissionChecker를 사용하기 위해 선언
    private PermissionsChecker checker;
cs


OnCreate에 다음 코드 추가

1
2
//Oncreate 에서 Checker를 호출
checker = new PermissionsChecker(this);
cs


그리고 다음 함수를 추가해준다.

1
2
3
    private void startPermissionsActivity() {
        PermissionsActivity.startActivityForResult(this, REQUEST_CODE, PERMISSIONS);
    }
cs


이제 팝업을 어디서 띄울지 결정해야했는데 카메라를 선택했을 때 띄우기로 결정

그래서 startActivityForResult(intent, PICK_FROM_CAMERA); 이 부분이 호출될 때 팝업이 뜨게 하였다.

권한 때문에 SecurtiyException 이 발생할 때

1
2
3
4
    //권한을 체크
    if (checker.lacksPermissions(PERMISSIONS)) {
        startPermissionsActivity();
    }
cs

이 부분을 추가해주어 권한 허용 유무를 체크하여 미허용상태면 팝업을 띄우도록 startPermissionsActivity() 을 요청한다.


추가된 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//카메라를 호출할 때 권한이 없으면 권한요청팝업이 뜨도록 설정
try{
    startActivityForResult(intent, PICK_FROM_CAMERA);
}catch (SecurityException e) {
    //권한을 체크
    if (checker.lacksPermissions(PERMISSIONS)) {
        startPermissionsActivity();
    }
    //토스트로 안내메시지도 같이 호출해보았습니다.
    e.printStackTrace();
    if(language.equals("en"|| language.equals("th"|| language.equals("vi"))
        Toast.makeText(getApplicationContext(),
                "check phone settinig >  application manager > app permission Camera approve",
                Toast.LENGTH_LONG).show();
    else
        Toast.makeText(getApplicationContext(),
                "설정 > 애플리케이션 관리자 >앱권한에서 카메라 권한을 허용해주세요",
                Toast.LENGTH_LONG).show();
}
cs


이렇게 해주면 카메라를 킬 때 허용 팝업이 노출된다.


외국인 블로거가 짜놓은 코드를 사용하였기 때문에 자세한 설명보다는 크게 크게 이해해보려고 하였다.

나중을 위해서 적어 놓은건데 빼 먹은 건 없나 모르겠다.


원래는 앱 시작하자마자 팝업 띄우려고 헀는데 뭔가 계속 안된다.. 

그래서 그냥 해당기능 켤 때  띄우는 걸로..


이런 걸 취미로 포스팅 하는 개발자들은 참 대단하다.

난 아무리 봐도 이해가 안가는데..





반응형

+ Recent posts