요즘 svelte랑 tailwind css 사용해서 조금씩 뭔가 만들어 보고 있는데

드롭다운 메뉴를 만들어 보려고 하던 와중

document is not defined 이런 에러가 나타났다.

문제가 되었던 코드는

1
2
3
4
5
6
7
$: {
    if (isOpen) {
      document.addEventListener('click', handleClickOutside);
    } else {
      document.removeEventListener('click', handleClickOutside);
    }
  }
cs

이 부분이었다

 

이 에러는 JavaScript가 브라우저 내에서 실행되는 환경에서만 document 객체가 정의되기 때문에

 Node.js 환경에서 실행될 때 발생하는 에러였다 SvelteKit 프로젝트에서는 서버에서 실행 중인

노드 코드에서는 document 객체를 사용할 수 없기 때문이었다.

그래서 스택오버플로우를 찾아보니 onMount를 사용해 해결 하는 방법이 있었다

 

1
2
3
4
5
6
7
8
9
10
11
import { onMount } from 'svelte';
 
$: {
    if (isOpen) {
      document.addEventListener('click', handleClickOutside);
    } else {
        onMount(() => {
            document.removeEventListener('click', handleClickOutside);
        });
    }
  }
cs

 

이렇게 온마운트 안에 다큐먼트를 사용하는 코드를 넣으면 해결이 된다.

onMount는 컴포넌트가 처음으로 DOM에 렌더링 될 때 실행되는 함수이다.

아마 위 코드를 사용할때 다큐먼트가 정의되지 않은 상태에서 에러가 발생하는듯 하다.

스벨트의 라이프 싸이클에 대해서 나중에 한번 정리를 해봐야겠다.

 

 

[JXLS] Cannot add merged region to sheet because it overlaps with an existing merged region 오류 해결 방법



제대로 //:3 이렇게 표기도 해준것 같은데


저런 에러가 난다면



셀병합되어진 부분이


//: 표기해준 곳 앞쪽에 또 있는지 확인해 보도록 하자





이런식으로 작성한 경우 NO 쪽에 병합된 셀이 있기 때문에 위와 같은 오류가 난다








이렇게 최초로 병합된셀이 나오는 부분에 :// 를 표기 하면 해당 오류를 해결 할 수 있다.

[안드로이드] duplicate entry 에러 해결 방법



Error:Execution failed for task 에러를 해결하니 이번엔 중복에러가 나타났다



duplicate entry: com/google/zxing/aztec/AztecDetectorResult.class


이런 에러인데


AztecDetectorResult 클래스가 중복이 되었다 뭐 그런 이야기다



구글의 라이브러리를 이것저것 참조하다보면 저런 에러가 흔히 나타나는듯 하다.


나같은 경우는


QR코드를 읽는 라이브러리와


안드로이드 카메라로 촬영한 이미지의 문자를 인식하는 라이브러리를 사용하던 중에


이것저것 끌어오다가 나타난 에러였다.



어느부분에서 중복되었는지 찾기가 쉽지는 않지만


어차피 앞쪽의 경로를 보면 대충 사이즈는 나오니까 잘 찾아보도록 하자









안드로이드 스튜디오의 프로젝트보기로 (안드로이드 보기론 안보임)


저 두군데를 뒤져보면 중복되는 녀석들이 나타난다



단순히 build.gradle에서 컴파일 명령을 지우는 것 만으로는 해결이 되지 않기 때문에


직접 저기서 파일 자체를 삭제 해주어야 한다.



물론 아무거나 지우다 일어나는 일은 책임 못진다.



build.gradle에서 명령줄만 삭제 해보고 



정상 동작 한다면 그떄 지우고 APK파일로 구동시켜 보도록 하자.




단순히 빌드할땐 나타나지 않다가 APK파일로 생성할때 나타나는 에러니까


정상동작하는걸 확인 후 삭제하는 것으로 



저 에러를 해결 하자



[톰캣] 서버 실행시 CMD창 바로 사라지는 문제 해결 방법






톰캣의 startup.bat 파일을 실행시킬때


cmd창이 떳다가 바로 사라지는 경우가 있었다.


뭐 에러 메세지라도 보여야 뭐가 잘못된건지 볼텐데


로그도 쌓이지 않고 그냥 사라져 버리는 문제에 대한 해결 방법이다.



일단 일반 cmd 창을 띄우고


톰캣이 설치된 경로로 이동한다



C:\apache-tomcat-7.0.76-windows-x64\apache-tomcat-7.0.76\bin



빈폴더 까지 이동 후


startup.bat  명령어를 입력하면


메세지가 나타나게 되는데


JRE_home environment variable is not defined correctly

a fatal exception has occurred. program will exit


요런 메세지가 나타난다.


JRE_home 가 아니라 JAVA_HOME 가 나타날 수도 있다


혹은 그외의 메세지가 나타날 수도 있으니


해당 메세지를 보고 오류를 해결하면 된다.


JRE_home environment variable is not defined correctly


이런 오류 메세지가 나타날떄의 해결 방법은


톰캣 bin 폴더의 catalina.bat 파일을 편집기로 열어서


JRE_HOME 나 JAVA_HOME의 경로를 


재지정 해주면 된다.



만약 JRE_HOME이나 JAVA_HOME 에 $JRE_HOME$ 이런 식으로


지정이 되어 있다면


시스템변수에 JAVA_HOME이나 JRE_HOME이 정상적으로


등록이 되어 있는지 확인 한다.



나의 경우는 JRE_HOME을 따로 지정하지 않았는데


톰캣 카탈리나에 JRE_HOME이 $JRE_HOME$ 으로 지정이 되어 있어서


시스템 변수에 JRE_HOME을 등록해 주고 난 후 오류를 해결 했다.



.




혹은 톰캣 윈도우를 실행 할때


지정된 서비스가 설치된 서비스로는 없습니다.


라는 메세지가 뜨는 경우


CMD창을 열어서


톰캣 빈폴더로 이동 후


service.bat install tomcat7


이라는 명령어를 입력하면


해결이 된다.






[안드로이드] 기기의 카메라에 오류가 생겼습니다. 기기를 재시작해야할수도있습니다. 해결방법 






QR코드나 BAR코드 리딩 기능을 넣거나


OCR 문자 인식 관련해서 API 23 이상부터 나타나는 에러이다


해결방법은 간단하다


API23 이전엔 그냥 매니페스트에 권한요청만 하면 되었는데


API23 이후부터는 아얘 권한 요청 팝업을 띄워서


허용을 받아야 한다.


해결방법은 어디서 찾아다가 프로젝트에 적용시켰는데 어디서 본건지 기억이 잘 안난다;





해결 방법은 아래와 같다


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
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        /**********권한 요청************/
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            /**
             * 사용자 단말기의 권한 중 "카메라" 권한이 허용되어 있는지 확인한다.
             *  Android는 C언어 기반으로 만들어졌기 때문에 Boolean 타입보다 Int 타입을 사용한다.
             */
            int permissionResult = checkSelfPermission(Manifest.permission.CAMERA);
 
 
            /** * 패키지는 안드로이드 어플리케이션의 아이디이다. *
             *  현재 어플리케이션이 카메라에 대해 거부되어있는지 확인한다. */
            if (permissionResult == PackageManager.PERMISSION_DENIED) {
 
 
                /** * 사용자가 CALL_PHONE 권한을 거부한 적이 있는지 확인한다. *
                 * 거부한적이 있으면 True를 리턴하고 *
                 * 거부한적이 없으면 False를 리턴한다. */
                if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
                    AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
                    dialog.setTitle("권한이 필요합니다.").setMessage("이 기능을 사용하기 위해서는 단말기의 \"카메라\" 권한이 필요합니다. 계속 하시겠습니까?")
                            .setPositiveButton("네"new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
 
                                    /** * 새로운 인스턴스(onClickListener)를 생성했기 때문에 *
                                     * 버전체크를 다시 해준다. */
                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                        // CALL_PHONE 권한을 Android OS에 요청한다.
                                        requestPermissions(new String[]{Manifest.permission.CAMERA}, 1000);
                                    }
                                }
                            })
                            .setNegativeButton("아니요"new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    Toast.makeText(MainActivity.this"기능을 취소했습니다", Toast.LENGTH_SHORT).show();
                                }
                            }).create().show();
                }
                // 최초로 권한을 요청할 때
                else {
                    // CALL_PHONE 권한을 Android OS에 요청한다.
                    requestPermissions(new String[]{Manifest.permission.CAMERA}, 1000);
                }
            }
            // CALL_PHONE의 권한이 있을 때
            else {
 
            }
        }
        /************권한요청 끝**************/
 
 
    }
 
 
 
/** * 권한 요청에 대한 응답을 이곳에서 가져온다. * *
     *  @param requestCode 요청코드 *
     *  @param permissions 사용자가 요청한 권한들 *
     *  @param grantResults 권한에 대한 응답들(인덱스별로 매칭) */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == 1000) {
            // 요청한 권한을 사용자가 "허용" 했다면...
            if (grantResults.length > 0 && grantResults[0== PackageManager.PERMISSION_GRANTED) {
                // 이곳에 허용했을때 실행할 코드를 넣는다
                // 근데 난 안넣음
 
            } else {
                // 거부했을때 띄워줄 
                Toast.makeText(MainActivity.this"권한요청을 거부했습니다.", Toast.LENGTH_SHORT).show();
            }
        }
    }
 
 
cs



액티비티에서 onCreate 부분에서 권한요청을 날려주면 된다.


메인액티비티에 걸어놨다면 어플을 실행할때 메세지가 뜰 것이다.


최적화를 위해서는


권한이 필요한 액션이 일어날때 권한요청을 물어보는 것이 좋다고 하는데


내가 하는 프로젝트는 웹뷰로 띄워놓고 하는거라


액티비티가 메인액티비티 하나뿐이다.


그리고 귀찮다.



만약 카메라 권한이 아니라 


전화걸기, 데이터접근 등의 권한이라면



Manifest.permission.CAMERA


이부분에서


CAMERA 만 원하는 퍼미션으로 바꿔주면 된다.


물론 하나만 바꾸는게 아니라 저거 적혀있는 모든 코드를 바꿔야 하는건 기본





[안드로이드] androidBridge they will not be visible in API 17 오류 해결 방법




none of the methods in the added interface(androidBridge) have been annotated with @android.webkit   they will not be visible in API 17 


이 오류의 해결 방법 입니다.



1. 원인


안드로이드 버전 API 17 이상부터 androidBridge 사용시 메소드에 어노테이션을 사용해야 정상 구동 하도록

바뀌어서 생기는 오류


2. 해결 방법


간단하게 androidBridge에서 사용하는 메소드위에 어노테이션을 추가 하면 됩니다.



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
package com.xxx.xxxx;
 
import android.os.Handler;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import java.text.SimpleDateFormat;
import java.util.Date;
 
/**
 * Created by Administrator on 2017-08-03.
 */
 
public class AndroidBridge {
    private final Handler handler = new Handler();
    private WebView mWebView;
    private DBHelper dbHelper;
    private  boolean newtwork;
 
    // 생성자
    public AndroidBridge(WebView mWebView, DBHelper dbHelper, boolean newtwork) {
        this.mWebView = mWebView;
        this.dbHelper = dbHelper;
        this.newtwork = newtwork;
    }
 
    // 네트워크 상태 확인
    @JavascriptInterface
    public void requestNetwork() { // must be final
        handler.post(new Runnable() {
            public void run() {
                Log.d("HybridApp""네트워크 상태 요청");
                mWebView.loadUrl("javascript:getNetwork("+newtwork+")");
            }
        });
    }
 
 
 
    @JavascriptInterface
    public void setMessage(final String arg) { // must be final
        handler.post(new Runnable() {
            public void run() {
                Log.d("HybridApp""setMessage("+arg+")");
                mWebView.loadUrl("javascript:getAndroidMessage('ANDROID -> JAVASCRIPT CALL!!')");
            }
        });
    }
 
 
}
cs


간단하게 다른거 볼 필요 없이


28번라인에 추가한 어노테이션을 29번같은 메소드 위에 선언하기만 하면 해당 오류가 해결 된다.



[React.js] cannot resolve 'react-transition-group' 오류 해결 방법



애니메이션 효과같은것들 적용 하려고 했던 react-transition-group이 서버 구동중 에러가 난다.


cannot resolve 'react-transition-group' 라는 에러메세지가 뜨길래


분명히 해당 패키지도 받았고 제대로 추가 시켰는데 왜 못찾는거지?


하고 검색을 해 보았다.


NPM에 올라온 글을 확인해 보니


This package is deprecated and will no longer work with React 16+. We recommend you use CSSTransitionGroup from react-transition-group instead.

In particular, its version 1.1.1 is a drop-in replacement for the last released version of react-addons-css-transition-group.

Run npm install --save react-transition-group@1.1.1, and replace the imports in your code


1
2
3
4
5
// Old 
import CSSTransitionGroup from 'react-addons-css-transition-group';
 
// New 
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
cs



라고 올라와 있었다


내용인 즉슨


리액트 16 이상 버전 부터는 기존에 사용하던걸 지원하지 않고 아얘 동작도 되지 않게 되었으니


새로운 다른걸 써라~


라고 하는 것이다.


쟤네가 설치하라는거 설치 하고 임포트 문장도 아랫것 처럼 바꾸면


오류는 해결이 된다.

react-router v4  browserHistory.push 오류 해결 방법


리엑트 라우터가 버전3에서 버전4로 올라감에 따라 


엄청나게 많은 것들이 변했다.


지금 새로운 프로젝트를 만들고 새로운 버전의


패키지들을 받은 후 코딩을 하려면 불과 한달전에 공부했던 코드들이 동작하지 않게 되었다.



망할.. 예전 webpack 버전1에서 2버전으로 올라가면서 바뀐거 찾아내느라 생고생 했는데



이젠 react-router가 문제다




Link를 사용하지 않고 바로 함수 내에서 리다이렉트를 시키는 방법으로


v3에서는 



1
browserHistory.push('/');
cs


이렇게 사용 하였으나


v4에선


Uncaught (in promise) ReferenceError: browserHistory is not defined


브라우저 콘솔창에 위와 같은 에러 메세지가 나타난다.


해결방법


생각보다 간단했다. 위의 코드 대신에


1
this.props.history.push('/');
cs



이렇게 사용해주면 된다.


물론 상단에 리엑트 라우터를 임포트시킬 필요는 없다.


그냥 기능이 넘어와 버린건지 정확하게는 알 수 없지만


깃터브를 뒤져보니 아래와 같은 의견을 찾았다.


걍 이전버전에서는 됐는데 v4에선 안된다 라는 말 같다.





@wyze @timdorr first of all thanks for the v4. I have a few questions and I could not find any solutions for this anywhere, so asking here.

In the earlier versions of the react-router, we could push the the URL using browserHistroy.push() method. When you use browserHistroy the query which you pass, will be transformed to the search key in the history object. The below is an example which used to work in earlier versions.

let query = {
  reportType: 'summary',
  timeZone: 'UTC'
}
  browserHistroy.push({
    pathname: 'some_path',
    query
  })

This will resolve the URL to - test.com/some_path/?reportType=summary&timeZone=UTC

In the current version (v4), it is not possible to pass the query object to the history.push() method.

  histroy.push({
    pathname: 'some_path',
    query
  })

This will resolve the URL to - test.com/some_path/

How to get this working with v4.

configuration resolve has an unknown property 'root' path 해결 방법


만약


webpack.config.js 파일에서


경로 작성시 불편함을 해소 하기 위해


resolve: {
root: path.resolve('./src')
},


위와 같은 코드를 사용후


configuration resolve has an unknown property 'root' path 


라는 메세지가 나오며 build할대 에러가 난다면


https://webpack.js.org/guides/migrating/


이곳을 참고 하자



resolve: {
alias: {
Components: path.resolve(__dirname, 'src/components/'),
Containers: path.resolve(__dirname, 'src/containers/')
}
},



이런식으로 alias를 추가해주고 추가한 별칭으로 경로를 사용하면 해결이 된다.



resolve문제인지 모르고 오전내내 삽질하다가


프로젝트를 처음부터 싹 밀어버리고 다시 하나씩 추가해가면서 빌드를 하던중


resolve 문제인걸 알고 한참동안 찾았다.


문서는 항상 진즉에 읽어보도록 하자


webpack1 에서 webpack2로 마이그레이션 되면서


바뀐 부분이 정말 너무 많다;;; 


지금 보고 있는 강좌가 webpack1 일때 작성되었던 강좌라 


설정방법들이 조금씩 다르다.


따로 가르쳐주는 사람이 없으니 정말 맨땅에 헤딩하면서 공부 하는데 


재밌긴 한데 어렵기도 하다.










 Using NoErrorsPlugin is deprecated 

해결 방법






아래와 같은 설정에서 이어지는 포스팅이다.


역시나 웹팩 플러그인에서 문제가 생긴다.


저 에러가 생겨도 일단 서버 구동은 되는듯 한데


대충 이제 저 플러그인 안쓰니까 다른걸로 대체해서 사용해라 라는 메세지이다


답은 메세지에 나와있다

NoErrorsPlugin

대신에


NoEmitOnErrorsPlugin

이걸 사용하면 된다.


1
2
3
4
5
plugins: [
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin()
    ],
cs


여기서


1
2
3
4
5
  plugins: [
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin()
    ],
cs



이렇게 바꿔주면 해결된다.


물론 그냥 저 NoErrorsPlugin 을 빼버려도 구동은 된다.



잘 정리되어있는 강좌라도 몇달전 강좌라면 지금 버전과 맞지 않는 부분이 생길 수 있으니


node.js나 react.js 같은 나온지 얼마 안된 라이브러리를 공부 할떈


해당 레퍼런스를 꼭 읽어봐야 하는 것 같다.


아니면 git나 stackOverFlow에서 검색을 하거나


역시 이바닥은 계속해서 공부해야 하나보다.


사실 강좌보고 예제 따라하면서 그냥 코드 복사해다가 붙여놓고 


우왕 신기하다 @_@ 


하고 넘어가고 지금까진 예제 코드들도 디코딩이 전혀 안된다


단지 이렇게 실행하다 만나는 에러들 해결방법 찾는 능력만 올라가는 것 만으로


공부가 된다고 위안을 삼는다.



일단 강좌들 한바퀴 쭉 돌고 혼자서 게시판이라도


만들어 볼 생각인데 정말 만만치가 않다


+ Recent posts