728x90

try~catch문에서 finally 블럭은 도대체 왜 필요한걸까?

try 블럭에서 예외가 발생하지 않으면 try-catch문 이후에 있는 코드가 실행이 될거고

try 블럭에서 예외가 발생해서 catch 블럭으로 갔다고 해도 가장 큰 범위인 Exception을 잡는 catch 블럭이 마지막에 무조건 있다면 어떤 예외가 발생하더라도 catch 블럭에 걸릴테니까 그 안에서 예외처리를 해주면 try-catch문 이후에 있는 코드가 실행이 될텐데

왜 굳이 finally가 필요할까? 하는 생각이 문득 들었다.

그래서 finally에 대해서 찾아보게 되었다.

 

일단 아래의 코드를 보자.

public class FinallyTest {
    public static void main(String[] args) {
        FinallyTest finallyTest = new FinallyTest();
        finallyTest.returnInFinally();
    }

    public void returnInFinally() {
        try {
            int n = 5 / 0;
            System.out.println("여기는 try 블럭");
        } catch (Exception e) {
            System.out.println("여기는 catch 블럭");
        } finally {
            System.out.println("여기는 finally 블럭");
        }
    }
}

이 코드의 결과는 어떻게 될까?

 

5를 0으로 나누는 순간 예외가 발생하여 catch 블럭으로 들어가게 되므로

System.out.println("여기는 try 블럭");

 위 코드는 실행되지 않고 catch 블럭으로 들어가게 된다.

catch블럭에서 "여기는 catch 블럭"을 출력하고 finally 블럭으로 들어가서 "여기는 finally 블럭"을 출력한다.

따라서 위 코드의 실행 결과는 아래와 같다.

여기는 catch 블럭
여기는 finally 블럭

 

그럼 이제 try 블럭과 catch 블럭에 return문을 넣어보자.

public class FinallyTest {
    public static void main(String[] args) {
        FinallyTest finallyTest = new FinallyTest();
        finallyTest.returnInFinally();
    }

    public void returnInFinally() {
        try {
            int n = 5 / 0;
            System.out.println("여기는 try 블럭");
            return;
        } catch (Exception e) {
            System.out.println("여기는 catch 블럭");
            return;
        } finally {
            System.out.println("여기는 finally 블럭");
        }
    }
}

위 코드의 실행 결과는 어떻게 될까?

1. catch 블럭에서 return을 해줬으니까 finally는 실행이 되지 않을 것이다.

2. return문을 만났어도 finally 블럭은 실행될 것이다.

.

.

.

.

.

.

.

정답은 2번이다.

위 코드를 실행하면 실행 결과는 아래와 같다.

여기는 catch 블럭
여기는 finally 블럭

try 블럭이 실행되고 나서 finally 블럭이 최종적으로 실행된다.

 

그러면 아래의 코드는 실행결과가 어떻게 될까?

public class FinallyTest2 {
    public static void main(String[] args) {
        FinallyTest2 finallyTest = new FinallyTest2();
        finallyTest.returnInFinally();
    }

    public void returnInFinally() {
        while (true) {
            try {
                System.out.println("여기는 try 블럭");
                break;
            } catch (Exception e) {
                System.out.println("여기는 catch 블럭");
            } finally {
                System.out.println("여기는 finally 블럭");
            }
        }
    }
}

while문을 들어가자마자 break;를 해줬다.

1. break문을 만났으니 바로 while문을 빠져나갈 것이다.

2. break문을 만났어도 finally 블럭은 실행될 것이다.

.

.

.

.

.

.

정답은 2번이다.

위 코드의 실행 결과는 아래와 같다.

여기는 try 블럭
여기는 finally 블럭

 

그러면 아래의 경우에는 어떨까?

public class FinallyTest3 {
    public static void main(String[] args) {
        try {
            FinallyTest3 finallyTest = new FinallyTest3();
            finallyTest.returnInFinally();
        } catch (Exception e) {
            System.out.println("여기는 main 메소드의 catch 블럭");
        }
    }

    public void returnInFinally() {
        try {
            int n = 5 / 0;
            System.out.println("여기는 try 블럭");
        } catch (Exception e) {
            System.out.println("여기는 catch 블럭");
            throw e;
        } finally {
            System.out.println("여기는 finally 블럭");
        }
    }
}

catch 블럭에서 잡은 예외 객체를 그대로 다시 던져줬다.

그렇다면 이 코드의 실행 결과는 어떻게 될까?

1. Java의 메소드에서 예외가 발생하면 바로 메소드를 빠져나가므로 finally 블럭은 실행되지 않을 것이다.

2. 예외가 발생해도 메소드를 바로 빠져나가지 않고 finally 블럭이 실행될 것이다.

.

.

.

.

.

.

.

정답은 2번이다.

위 코드의 실행 결과는 아래와 같다.

여기는 catch 블럭
여기는 finally 블럭
여기는 main 메소드의 catch 블럭

 

그럼 마지막으로 아래의 코드를 보자.

public class FinallyTest4 {
    public static void main(String[] args) {
        try {
            FinallyTest4 finallyTest = new FinallyTest4();
            int result = finallyTest.returnInFinally();
            System.out.println("result: " + result);
        } catch (Exception e) {
            System.out.println("여기는 main 메소드의 catch 블럭");
        }
    }

    public int returnInFinally() {
        try {
            int n = 5 / 0;
            System.out.println("여기는 try 블럭");
            return 0;
        } catch (Exception e) {
            System.out.println("여기는 catch 블럭");
            throw e;
        } finally {
            System.out.println("여기는 finally 블럭");
            return 1;
        }
    }
}

앞의 코드에서 catch 블럭에서 예외가 발생한 경우에도 finally 블럭이 실행되었다고 했으니 finally 블럭의 "여기는 finally 블럭"이 출력될 것이다.

그러면 메인 메소드에서 catch 블럭에 걸려서 "여기는 main 메소드의 catch 블럭"을 출력할까? 아니면 result: 1을 출력할까?

1. returnInFinally 메소드에서 예외를 던졌으므로 main 메소드의 catch 블럭이 실행될 것이다.

2. finally에서 1을 return 했으니 main 메소드에서 정상적으로 1을 return 받고 catch 블럭에 들어가지 않는다.

.

.

.

.

.

.

.

.

정답은 2번이다.

위 코드의 실행 결과는 다음과 같다.

여기는 catch 블럭
여기는 finally 블럭
result: 1

 

https://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html

 

The finally Block (The Java™ Tutorials > Essential Java Classes > Exceptions)

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

위 링크는 Java의 공식 튜토리얼 사이트인데

예상치 못한 예외가 발생한 경우에도 finally 블럭이 실행되는 것을 보장한다.

return, continue, break로 인해 cleanup 코드가 실행되지 않는 일을 막을 수 있다.

라고 나와있다.

 

그러니 이 글의 초반에서 finally가 왜 필요할까? 하는 의문이 해소되었다.

return, continue, break, 예외 발생 등으로 메소드가 종료되어도 finally 블럭의 코드는 실행된다. (return 이후에 finally 블록이 실행된다)

return, break 등을 만나 메소드나 블록을 빠져나가는 경우에도 무조건 실행되어야 하는 코드가 있는 경우 매우 유용할 것 같다.

 

그런데 조금 놀란 것은 마지막 코드(FinallyTest4)에서 finally에서 return을 하는 바람에 앞에서 발생했던 예외가 무시되었다는 것이다.

하나의 메소드에서는 return과 예외를 던지기 2개를 모두 할 수 없다.

return을 하거나 예외를 던지거나 둘 중 하나만 가능하다.

 

위의 코드에서는 catch 블럭에서 예외를 throw 했으나 메소드 종료 전에 finally 블럭이 실행되면서 예외를 throw 하는 것이 아니라 1을 return 하게 되었다. finally문에서 return을 하는 바람에 throw한 Exception을 잃어버리게 된 것이다.

catch 블럭에서 예외를 던진 것이 아니라 return을 한 경우에도 해당 return 값은 무시되고 finally에서 return 한 값이 최종적으로 return 될 것이다.

try문에서 예외가 발생하지 않아 return 0; 부분이 실행되었더라도 0을 반환하는 것이 아니라 finally문에 걸려서 1을 반환할 것이다.

 

따라서 finally 블럭에서 return 하는 것은 의도치 않은 결과를 얻을 수 있으니 하지 않는 것을 권장한다.

인텔리제이에서도 'finally' block can not complete normally, 'return' inside 'finally' block 이라며 경고 메시지가 뜬다.

finally 블럭 안에서 return을 하는 것은 의도했다 하더라도 던져진 예외를 가릴 수 있고 디버깅을 어렵게 할 수 있다.

이제 finally 블럭 안에서 return을 하는 것은 안 된다는 것은 알았다.

그러면 아래와 같은 코드는 어떨까?

try 블럭 이후에 finally 블럭이 실행될텐데 5를 반환할까? 10을 반환할까?

public class FinallyTest5 {
    public static void main(String[] args) {
        FinallyTest5 finallyTest = new FinallyTest5();
        int result = finallyTest.returnInFinally();
        System.out.println("result: " + result);
    }

    public int returnInFinally() {
        int n = 5;
        try {
            System.out.println("여기는 try 블럭");
            return n;
        } finally {
            System.out.println("여기는 finally 블럭");
            n = 10;
        }
    }
}

위 코드의 실행결과는 아래와 같다.

여기는 try 블럭
여기는 finally 블럭
result: 5

finally 블럭에서 n을 10으로 바꿔줘도 5를 반환한다.

인텔리제이에서도 finally 블럭에서 n에 10을 할당해주는 부분에서 n은 회색으로 표시가 되고 아래와 같은 경고 메시지가 뜬다.

그럼 아래와 같이 참조자료형의 경우는 어떻게 될까?

public class FinallyTest6 {
    public static void main(String[] args) {
        FinallyTest6 finallyTest = new FinallyTest6();
        StringBuilder result = finallyTest.returnInFinally();
        System.out.println("result: " + result);
    }

    public StringBuilder returnInFinally() {
        StringBuilder str = new StringBuilder("five");
        try {
            System.out.println("여기는 try 블럭");
            return str;
        } finally {
            System.out.println("여기는 finally 블럭");
            str.setLength(0);
            str.append("ten");
        }
    }
}

StringBuilder 객체의 특정 인스턴스 변수를 "five"로 초기화했다가 try문에서 해당 객체를 return 하고 그 이후 finally에서 해당 필드의 값을 "ten"으로 바꿔줬다. (객체를 새로 생성해서 재할당 한 것이 아니라 객체는 그대로고 특정 필드의 값만 바꿨다.)

이 경우 실행 결과는 아래와 같다.

여기는 try 블럭
여기는 finally 블럭
result: ten

finally 블럭에서 값을 바꾼 것이 영향을 주었다.

 

인텔리제이에서도 finally 블럭에서 str이 회색 처리가 되지 않았다. (물론 finally 블럭에서 str = new StringBuilder("ten"); 이렇게 재할당을 했다면 str도 회색 처리가 되었을 것이다)

 

FinallyTest5처럼 기본 자료형인 경우와 FinallyTest6처럼 참조 자료형인 경우는 또 다르다.

return도 Java의 pass by value와 똑같다고 보면 된다.

이 사실을 염두에 두고 코드를 작성할 때 주의하면 좋을 것 같다.

 

그럼 finally 블록이 실행되지 않는 경우는 언제일까?

try 블럭이나 catch 블럭에서 이렇게 System.exit()을 하는 경우에는 finally 블럭이 실행되지 않는다.

System.exit(1);

 

System.exit() 메소드를 호출하면 아예 이 프로그램 자체가 종료되기 때문이다.

finally가 실행되지 않는 경우는 이 경우가 유일하다.

 

 

출처

https://stackoverflow.com/questions/3861522/do-you-really-need-the-finally-block

https://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html

https://stackoverflow.com/questions/48088/returning-from-a-finally-block-in-java

https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.20.2

https://milkye.tistory.com/367

https://stackoverflow.com/questions/7297937/does-java-return-by-reference-or-by-value

https://stackoverflow.com/questions/4205362/java-beginner-returning-an-object-is-it-returned-as-a-constant-reference-o

728x90
728x90

스프링 프레임워크를 처음부터 다시 깊이 있게 제대로 공부를 하려고 https://www.inflearn.com/course/스프링-입문-스프링부트 강의를 듣기 시작했는데 시작부터 난관에 부딪혔다.

빌드가 안 된다.

 

분명히 이렇게 저 인텔리제이의 버튼을 눌러서 실행 하는 것은 잘만 됐었다. 

그리고 이 build 버튼을 눌러서 빌드를 하는 것도 잘 성공했었다.

빌드 성공

 

그런데 이렇게 터미널에서만 하면

invalid source release: 11 이라면서 빌드에 실패했다.

검색을 해보니까 Settings와 Project Structure에 있는 자바나 JDK 버전을 다 11로 맞추라고 해서

굳이 11로 맞추지 않아도 되는 것까지도 무조건 다 11로 고쳤다. 이렇게 말이다.

그런데도 결과는 똑같다.

 

인텔리제이 IDE를 통해서 빌드를 하면 잘 되고 직접 터미널로 하니까 안 되니까 문제는 build.gradle에 있지 않을까? 라는 생각을 했다.

왜냐하면 내가 Build and run using을 Gradle이 아니라 IntelliJ IDEA로 해놨기 때문이다.

그래서 문제는 build.gradle에 있을 거라고 생각하고 build.gradle을 봤다.

sourceCompatibility가 '11'로 되어 있는데 원인이 이것인지 알아보기 위해 sourceCompatibility를 '17'로 고쳐보았다.

그랬더니 이번에는 invalid source release: 17 이라는 오류가 났다.

그렇다면 원인은 sourceCompatibility라는 것이 확실해졌다.

그래서 아예 sourceCompatibility='11' 이 부분을 지워봤다.

그랬더니 이렇게 빌드에 성공했다.

왜 그런지는 모르겠다.

일단 검색을 해서 나오는 어떠한 방법으로도 안 됐는데 해결을 해서 너무 다행이다.

build.gradle 파일에서 sourceCompatibility가 어떤 역할을 하는 건지 좀 더 알아봐야겠다.

728x90
728x90

 

ufw를 enable할 때 분명히 얘가 이렇게 친절히 물어봤다.

Command may disrupt existing ssh connections. Proceed with operation?

나는 아무렇지도 않게 y를 눌렀고

그 이후로 서버에 접속이 되지 않았다.

ssh: connection to host {IP 주소} port 22: Connection timed out

이럴수가...

ufw를 enable 시켜서 이런 문제가 발생한 것이기 때문에 이를 해결하려면 ufw를 다시 disable 시키면 된다.

그런데 서버에 접속을 해야 disable을 하든지 말든지 할텐데 서버 접속이 안 되는데 어쩌란 말이냐!!

 

하지만 구글링을 해보니 다 방법이 있다.

 

일단 접속이 불가능해진 해당 인스턴스를 중지시킨다. (종료 아니고 중지다. 종료를 하면 인스턴스가 삭제되니 주의할 것)

 

번역이 참... 중지 인스턴스?

주황색 중지 버튼을 누른다.

 

 

인스턴스를 중지하면 인스턴스 상태가 '중지중'이다가

 

조금 기다리면 '중지됨' 상태가 된다.

 

 

왼쪽 메뉴 중 아래쪽에 Elastic Block Store(EBS)의 볼륨 메뉴에 들어간다.

 

방금 중지시킨 해당 인스턴스와 연결되어 있는 볼륨을 분리한다. (작업 > 볼륨 분리)

 

주황색 '분리' 버튼을 누른다.

 

성공적으로 분리되었다.

 

다시 인스턴스 메뉴로 들어가서 '인스턴스 시작' 버튼을 눌러 새 인스턴스를 하나 생성한다. (임시로 사용할 인스턴스이다)

나는 pem 파일 다시 받고 하면 귀찮으니까 키페어는 똑같은 키페어를 쓰고 보안그룹도 그대로 선택했다.

(우리집 IP랑 내가 자주 가는 카페 IP만 ssh 접속을 허용해놓은 보안그룹이다.)

 

스토리지 구성 부분이 중요한데, 볼륨 유형 (gp2, gp3 등)을 기존 인스턴스와 똑같이 지정해주어야 한다.

 

잠깐 쓰고 삭제할 임시 인스턴스이기 때문에 temp-server라는 이름으로 생성했다.

아직은 인스턴스 상태가 대기중이다.

조금만 기다리면 '실행중' 상태가 된다.

 

다시 볼륨 메뉴로 간다.

아까 기존 인스턴스에서 분리한 볼륨이 '사용 가능' 상태로 되어 있고, 이번에 인스턴스를 새로 만들면서 생긴 볼륨이 '사용중'으로 되어 있는 것을 볼 수 있다.

 

'사용 가능'인 기존 볼륨을 이번에 새로 만든 임시 인스턴스와 연결해준다.

기존 볼륨을 체크하고 작업 > 볼륨 연결을 클릭한다.

 

연결할 인스턴스로 방금 새로 만든 임시 인스턴스를 선택한다.

 

주황색 '볼륨 연결' 버튼을 누른다.

 

새로 만든 임시 인스턴스에 접속한다.

나는 키페어를 그대로 했기 때문에 기존에 있던 pem 파일을 사용하여 접속했다.

아래 명령어를 입력한다.

sudo lsblk

xvdf 아래에 xvdf1이 보인다.

 

새로운 디렉토리를 생성하고

xvdf1을 해당 디렉토리에 마운트시킨다.

mkdir mnt
sudo mount /dev/xvdf1 ./mnt

 

아래 명령어로 방금 만든 mnt 디렉토리의 /etc/ufw/ufw.conf 파일을 vi editor로 연다.

sudo vi /mnt/etc/ufw/ufw.conf

 

ENABLED=yes라고 되어 있는 것이 보인다.

이 부분을 no로 수정하고 저장한다.

이제 드디어 ufw를 disable 시키는 데 성공한 것이다.

 

이제 임시로 생성했던 인스턴스를 중지시킨다. (조금 기다리다가 새로고침을 하면 인스턴스 상태가 '중지됨'이 된다)

 

임시 인스턴스와 임시로 연결했던 원래 볼륨을 볼륨 분리해준다. (작업 > 볼륨 분리)

 

조금 더 기다리면 다시 볼륨 연결 버튼이 활성화된다.

기존 볼륨을 선택하고 작업 > 볼륨 연결 버튼을 누른다.

 

인스턴스는 기존에 있던 인스턴스를 선택한다.

 

디바이스 이름은 자동으로 채워져 있을 것이다. 그런데 이대로 볼륨을 연결하면 안 된다!! 디바이스 이름을 바꿔줘야 한다.

 

루트 볼륨은 /dev/sda1이라는 이름을 사용해야 한다고 한다.

/dev/sda1로 디바이스 이름을 고치고 '볼륨 연결' 버튼을 누른다.

 

다시 인스턴스 메뉴로 돌아와 기존 인스턴스를 시작한다. (인스턴스 상태 > 인스턴스 시작)

 

만약 아래와 같이

Invalid value {인스턴스 ID} for instanceId. Instance does not have a volume attatched at root (/dev/sda1)

라는 에러가 나면서 인스턴스 시작에 실패한다면 볼륨을 연결할 때 디바이스 이름을 /dev/sda1로 하지 않은 것이다.

다시 볼륨을 분리했다가 볼륨을 연결할 때 디바이스 이름을 /dev/sda1로 해서 다시 연결하면 된다.

 

인스턴스 상태가 '대기중'이다가 '실행중'으로 바뀌면 ssh로 접속을 시도한다. 이제 성공적으로 접속이 된다.

 

인스턴스 상태가 '실행중'으로 바뀌었어도 너무 바로 접속하면 이렇게 Connection refused가 뜰 수도 있는데, 잠시 기다렸다가 접속하면 된다.

 

마지막으로 임시로 생성했던 인스턴스를 삭제한다. (인스턴스 상태 > 인스턴스 종료)

 

그러고 나서 다시 ufw를 사용하고 싶다면

sudo ufw allow ssh

이렇게 먼저 ssh를 allow 해준 뒤

sudo ufw enable

ufw를 enable 시켜주면 된다.

 

출처

https://yvvyoon.github.io/ubuntu/ufw-enable-ssh-timeout/

https://gksdudrb922.tistory.com/202

https://zel0rd.tistory.com/192

 

728x90
728x90

Java 개발을 하다가 jdbc로 MySQL DB와 연결을 하는데 the last packet sent successfully to the server was 0 milliseconds ago 이런 에러가 나는 경우가 있다.

이 경우 DB URL 뒤에 ?useSSL=false을 추가해주면 된다.  MySQL은 SSL 설정이 default가 true인데 SSL 연결을 한다는 설정을 false로 바

것이다.

 

 

예를 들어 URL이 아래와 같다면

jdbc:mysql://어쩌구저쩌구.ap-northeast-2.rds.amazonaws.com:3306/my_db

아래와 같이 뒤에 추가해주면 된다.

jdbc:mysql://어쩌구저쩌구.ap-northeast-2.rds.amazonaws.com:3306/my_db?useSSL=false

파라미터가 여러 개라면 아래와 같이 &로 연결해주면 된다.

jdbc:mysql://어쩌구저쩌구.ap-northeast-2.rds.amazonaws.com:3306/my_db?useSSL=false&characterEncoding=UTF-8&serverTimeZone=Asia/Seoul

 

728x90
728x90

정말 간만에 리액트로 프론트엔드 개발을 하려고 했는데 시작부터 발목이 잡혔다.

create react-app을 이용해서 프로젝트를 만드는데 이런 에러가 났다.

이렇게 에러가 나면서 package.json 파일에는 아무런 내용이 없었다. 그래서 당연히 yarn start 이런 명령어들이 되지 않았다. 수동으로 package.json에 내용을 채워봐도 안 되는 것은 마찬가지였다.

 

그래서 열심히 구글링을 해보니 아래와 같은 글을 발견했다.

 

Create-react-app error : internal/modules/cjs/loader.js:311 , when using npx to create a react app

When using npx create-react-app to create a new react project, the process stops with error : internal/modules/cjs/loader.js:311. internal/modules/cjs/loader.js:311 throw err; ^ Error: Cannot find ...

stackoverflow.com

yarn create react-app test-app

이렇게 yarn으로 생성하지 말고

 

npx create-react-app test-app

이렇게  npx로 생성하라는 것이었다.

 

이대로 했더니 에러가 나지 않고 성공했다.

 

그러면 package-lock.json 파일이 생성되어 있을 것이다.

 

그런데 나는 yarn을 사용하고 싶다면?

 

package-lock.json을 삭제한 후에

yarn install

을 해주면 된다.

 

그러면 package-lock.json 대신 yarn.lock이 생겨있을 것이다.

 

이렇게 해서 yarn을 포기하지 않으면서 에러도 해결했다.

 

728x90
728x90

상태바(상단바)란?

스마트폰에서 시간, 배터리 잔량, 시스템 상태, 각종 알림 등을 표시하는 상단에 위치한 바

상태바 (상단바)

안드로이드에서는 상태바의 색상, 글자색 등을 제어할 수 있다고 한다. (아이폰은 불가능하다고 한다.)

 

상태바를 제어하기 위해서는 먼저 리액트 네이티브의 'StatusBar'를 import 해야 한다.

import { StatusBar } from 'react-native';

위 코드로 StatusBar를 import 하면 된다.

 

상태바 색상 변경하기

기본 상태바

위 상태바의 색을 흰색으로 바꾸고 싶다면?

StatusBar.setBackgroundColor("white");

위 코드를 추가하면 상태바의 배경색이 흰색이 된다.

상태바 배경색 흰색으로 변경

상태바의 배경색은 흰색이 되었지만 글자도 흰색이라 글자가 잘 보이지 않는 것을 볼 수 있다.

(다행히 충전중이라 번개표시 정도는 보인다..)

 

이 경우 StatusBar의 setBarStyle 함수를 이용하여 상태바의 글자색을 변경할 수 있다.

StatusBar.setBarStyle('dark-content');

상태바 글자색이 어둡게 바뀜

"dark-content"로 하면 상태바의 글자 색이 어두운 색이 되고,

"light-content"로 하면 상태바의 글자 색이 밝은 색이 된다.

 

아래와 같이 배경이 검은색인 경우

StatusBar.setBarStyle('light-content');

위 코드로 상태바의 글자색을 밝게 바꾸면

이렇게 글자색이 밝아져 상태바의 글자를 잘 알아볼 수 있다.

728x90
728x90

오늘 어떤 라이브러리가 필요해서 Gradle 파일에 의존성을 추가해주었다.

이 코끼리를 클릭하여 Sync도 잘 해서 빌드도 성공적으로 했다.

 

그런데 프로그램을 실행하려고 하니

Execution failed for task ':compileJava'. > Could not resolve all files for configuration ':compileClasspath'. > Could not find com.github.shin285:KOMORAN:3.3.4. Searched in the following locations: - https://repo.maven.apache.org/maven2/com/github/shin285/KOMORAN/3.3.4/KOMORAN-3.3.4.pom If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration. Required by: project : Possible solution: - Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html

 

이런 에러가 났다.

 

찾아보니 아래와 같이 Gradle 파일의 allproject의 repositories에 아래 코드를 추가해야 한다고 한다.

maven { url 'https://jitpack.io' }

 

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

이렇게 코드를 추가하니 오류가 발생하지 않고 잘 돌아갔다.

 

출처: https://www.python2.net/questions-666798.htm

728x90
728x90

스프링부트 Thymeleaf에서는 변수를 쓸 때 ${변수} 이런 식으로 쓸 수 있다.
내가 오늘 개발을 하다가

<td th:text="${product.price}"></td>

이런 코드를 작성했다.
그런데 "800"이 아니라 "800원"으로 보이면 좋을 것 같아서

<td th:text="${product.price}원"></td>

코드를 이렇게 수정했다.

그랬더니 이런 에러가 났다.

ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/파일이름.html]")] with root cause
org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "${product.price}원"


변수를 쓸 때 ${}를 쓰는거라고 해서 그냥 문자열은 ${} 바깥쪽에 적어줬는데 이렇게 하면 안 되는 것이었다.
아래의 코드처럼 작성해야 한다.

<td th:text="|${product.price}원|"></td>

이렇게 큰 따옴표 안쪽에 |을 넣어주었더니 에러 없이 잘 돌아갔다.

출처: https://velog.io/@susu1991/Thymeleaf#%EB%A6%AC%ED%84%B0%EB%9F%B4

728x90
728x90

스프링 부트 개발을 하다가 아래와 같은 에러가 났다.

2021-07-13 21:32:15.194 ERROR 20836 --- [nio-8080-exec-1] org.thymeleaf.TemplateEngine             : [THYMELEAF][http-nio-8080-exec-1] Exception processing template "index.html": Error resolving template [index.html], template might not exist or might not be accessible by any of the configured Template Resolvers

 

org.thymeleaf.exceptions.TemplateInputException: Error resolving template [index.html], template might not exist or might not be accessible by any of the configured Template Resolvers

at org.thymeleaf.engine.TemplateManager.resolveTemplate(TemplateManager.java:869) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]

at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:607) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]

at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]

at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]

at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:366) ~[thymeleaf-spring5-3.0.12.RELEASE.jar:3.0.12.RELEASE]

at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:190) ~[thymeleaf-spring5-3.0.12.RELEASE.jar:3.0.12.RELEASE]

at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1396) ~[spring-webmvc-5.3.8.jar:5.3.8]

at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1141) ~[spring-webmvc-5.3.8.jar:5.3.8]

at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080) ~[spring-webmvc-5.3.8.jar:5.3.8]

at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.8.jar:5.3.8]

at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.8.jar:5.3.8]

at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.8.jar:5.3.8]

at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.48.jar:4.0.FR]

at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.8.jar:5.3.8]

at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.48.jar:4.0.FR]

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.48.jar:9.0.48]

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.8.jar:5.3.8]

at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.8.jar:5.3.8]

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.8.jar:5.3.8]

at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.8.jar:5.3.8]

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.8.jar:5.3.8]

at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.8.jar:5.3.8]

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1723) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]

at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]

at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.48.jar:9.0.48]

at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

 

웹 브라우저로 들어가봐도 이렇게 떴다.

해결방법은 간단했다.

html 파일을 templates 폴더로 옮기면 된다. 나는 index.html을 static에 넣어놨었는데 index.html 파일을 templates 폴더로 옮기니 해결되었다.

출처: https://stackoverflow.com/questions/31944355/error-resolving-template-index-template-might-not-exist-or-might-not-be-acces

 

Error resolving template "index", template might not exist or might not be accessible by any of the configured Template Resolver

This question has been asked before but I did not solve my problem and I getting some weird functionality. If I put my index.html file in the static directory like so: I get the following error in...

stackoverflow.com

 

728x90
728x90

Java에는 Javadoc이라는 문서화 툴이 있어서 문서를 쉽게 만들 수 있다.
사용법은 간단하다. 변수, 메소드, 클래스 선언부 바로 위에 /**로 시작하는 여러줄 주석을 달면 된다.
/** 까지만 치면 인텔리제이가 알아서 자동완성해준다.

패키지 주석은 해당 패키지에 package-info.java라는 파일을 만들고 그 파일에 주석을 달면 된다.
인텔리제이에서는 패키지에 오른쪽 마우스를 클릭하고 New를 누르면 package-info.java를 만들 수 있게 되어있다.

그러면 이렇게 생겼을 것이다.

이 코드 위에 여러줄 주석으로 패키지에 대한 설명을 달면 된다.


클래스 주석은

이렇게 달면 된다.
만약 lombok과 같은 어노테이션이 있다면 아래와 같이 어노테이션 위에 주석을 달면 된다.

메소드 주석은

이렇게 달면 된다. 메소드를 완성하고 /** 까지만 치면 @param, @return, @throws까지 자동으로 만들어준다.
맨 위에는 메소드에 대한 설명을 적으면 된다. (급조하느라 아주 말도 안 되는 이상한 메소드이다.)
@param에는 매개변수에 대한 설명을, @return에는 반환값에 대한 설명을, @throws에는 던지는 예외에 대한 설명을 적는다.

주석을 다 달았으면 이제 문서를 생성하면 된다.
Tools에 Generate JavaDoc을 클릭한다.


그리고 한글 깨짐 방지를 위해 Other command line arguments에

-encoding UTF-8 -charset UTF-8 -docencoding UTF-8

을 입력한다.


그리고 OK를 누른다.

그러면

이렇게 생성이 되는 것을 볼 수 있다.

아까 Open generated documentation in browser에 체크를 했기 때문에 문서가 바로 웹 브라우저로 열릴 것이다.
만약 열리지 않는다면 아까 지정해준 경로에서 index.html을 열면 된다.

자바 공식 문서와 똑같이 생겨서 굉장히 그럴듯해보인다.

문서를 만드는 또 다른 방법이 있다.
build.gradle에

javadoc { 
    source = sourceSets.main.java.srcDirs 
    options.encoding = 'UTF-8' 
}

다음과 같은 코드를 적어준다.

그러고 나서 터미널을 열어준다.

경로가 해당 프로젝트 디렉터리인지 꼭 확인한다.
여기에서

이렇게 입력하고 엔터를 치면

성공적으로 문서가 만들어진다.

만들어진 문서는 build 폴더의 docs 안에 javadoc 안에 있다.

그런데 이렇게 문서를 만들고 나서 보니 lombok(롬복)의 @Getter, @Setter, @ToString, @NoArgsConstructor, @AllArgsConstructor 등으로 만들어진 메소드는 문서에 나타나지 않는 것을 알 수 있을 것이다.

이는 delombok을 이용하여 간단히 해결할 수 있다.
build.gradle에 다음과 같은 코드를 추가한다. 아까 build.gradle에 javadoc 부분을 추가했었다면 그 부분을 아래와 같이 변경한다.

javadoc {
    dependsOn delombok 
    source = file("$buildDir/delombok") 
    options.encoding = 'UTF-8' 
    failOnError = false 
}

그러고 나서

을 하면 된다.

728x90

+ Recent posts