자바스크립트를 브라우저에서 디버깅 할 때 "debugger"를 코드에 넣어 중단점을 설정할 수 있다.
그러나 평범한 html, js 기반이 아닌 프레임워크를 사용할 경우 해당 기능이 브라우저에서 안되는 경우가 있다. 이 경우 안되는 이유는 IDE 문제나 코드의 문제가 아니라 브라우저 설정의 문제이다.

크롬의 경우 
개발자도구(F12) => 설정(톱니바퀴) -> 무시목록(Ignore list) 설정
에서 해당 js 파일의 경로의 정규식을 등록하거나, 무시 옵션을 잠시 끄고 디버깅을 맞춘 후 원상복구 하면 된다. 

해당 옵션이 왜 존재하는지 검색한 결과 라이브러리 등의 코드 내부에 "debugger" 코드가 존재하여 원하지 않는 중단점에서 멈추는 경우들을 방지하는 것으로 확인하였다.

도커 환경에서 배포 + 폰트가 필요한 라이브러리 사용 시 "Error while loading available fonts" 오류가 발생할 때가 있다.

도커 jdk 컨테이너에는 폰트 파일이 없어서 그런 경우이다.


프로젝트에서 캡차 라이브러리, 제스퍼리포트 라이브러리를 사용할 때 폰트 문제 발생을 경험하였다.

docker compose 파일에 아래와 같이 entrypoint 를 추가하여 컨테이너 시작시 폰트 파일을 설치하도록 하여 해결하였다.

entrypoint: [ "apk add --no-cache fontconfig ttf-freefont && 다른 커맨드" ]

코딩테스트 문제를 풀 다 시간 초과를 해결했던 경험에 대한 기록이다.

백준의 텀프로젝트 문제를 풀던 중(https://www.acmicpc.net/problem/9466)
내가 작성한 코드가 충분히 최적화 되었다고 생각했음에도 계속 시간 초과가 발생하였다.
관련하여 문제를 찾던 중 자바의 배열 생성이 시간 초과의 원인이 될 수 있다는 글을 발견하고, 해당 부분을 수정하여 통과하였다.


    private static int solution() throws IOException {
        int studentNum = Integer.parseInt(br.readLine());
        int[] team = new int[studentNum + 1];
        String[] input = br.readLine().split(" ");
        for(int i = 1; i <= studentNum; i++){
            team[i] = Integer.parseInt(input[i-1]);
        }

        checked = new boolean[studentNum + 1];
        result = studentNum;

        for(int i = 1; i <= studentNum; i++){
            if(checked[i]) continue;
            // 배열 초기화
            visited = new int[studentNum + 1];
            findTeam(team, i, 1, visited);
        }

        return result;
    }

    private static void findTeam(int[] team, int student, int seq, int[] visited){
        if(checked[student]) return;
        checked[student] = true;
        visited[student] = seq;

        int next = team[student];
        if(visited[next] != 0){
            result -= (seq - visited[next] + 1);
        }else{
            findTeam(team, next, seq + 1, visited);
        }
    }

 

시간 초과가 나던 시점의 내 코드는 위와 같았으며, 완전 탐색을 위하여 탐색 방문 배열을 new 명령어로 생성하고 있었다. 해당 배열의 크기는 최대 100001의 크기를 갖는 문제이다.

 

    private static int solution() throws IOException {
        int studentNum = Integer.parseInt(br.readLine());
        int[] team = new int[studentNum + 1];
        StringTokenizer st = new StringTokenizer(br.readLine());
        for(int i = 1; i <= studentNum; i++){
            team[i] = Integer.parseInt(st.nextToken());
        }

        checked = new boolean[studentNum + 1];
        int[] visited = new int[studentNum + 1];
        result = studentNum;
        for(int i = 1; i <= studentNum; i++){
            if(checked[i]) continue;
            findTeam(team, i, 0, visited);
        }

        return result;
    }

    private static void findTeam(int[] team, int student, int seq, int[] visited){
        if(checked[student]) return;
        seq++;
        checked[student] = true;
        visited[student] = seq;

        int next = team[student];
        if(visited[next] != 0){
            result -= (seq - visited[next] + 1);
        }else{
            findTeam(team, next, seq, visited);
        }

        // dfs 내부에서 사용후 값 원상복구
        visited[student] = 0;
    }

}

 

시간 초과를 해결한 코드는 위와 같다. dfs를 반복하기 이전에 생성한 배열의 값을 new 가 아닌 직접 초기화 하여 배열을 사용하였다. 참고한 글(https://okky.kr/questions/1450047)에 따르면 배열을 생성한다는 것은 새로운 객체의 메모리에 할당 받는 부분, java의 경우 해당 배열의 초기 값을 초기화하는 부분 등으로 인하여 런타임 실행 시간이 늘어날 수 있다고 한다. 단순한 코드의 차이였지만 객체 생성의 효율에 대해 고민할 수 있었다.

프로젝트에서 동료 개발자 분이 라이브러리 추가를 하는 상황에 발생했던 트러블에 대한 기록이다.

프로젝트에서 필요한 라이브러리가 생겨 maven 을 통하여 추가하려 하였으나 maven 리포터지터리의 문제인지 제대로 받아지지 않았다. 따라서 해당 라이브러리 jar 을 직접 받아 프로젝트 내부에 추가하였다.

로컬에서 실행시 문제 없이 잘 동작하였으나 서버에 올리기 위해 빌드 후 배포 했을때 문제가 발생하였다. 해당 파일의 클래스를 찾을 수 없어 스프링 빈이 주입되지 않는다는 오류를 만나게 되었다. 

원인을 파악하기 위해 빌드 파일을 확인한 결과 해당 라이브러리 파일이 누락되어 빌드가 진행된다는 사실을 확인하게 되었다. 이것저것 시도하다 결국 해결하게 되었는데, 해결 방법의 정확한 이유는 파악하지 못하였다... 라이브러리 파일이름이 "라이브러리-1.0.1.jar" 형태였는데 뒷 부분의 버젼 부분을 변경하여 "라이브러리.jar"로 변경 한 이후에는 빌드시 제대로 포함되는 것을 확인하였다.

대충 추측을 하자면 "-1.0.1" 과 같이 하이픈(-)을 사용할 경우 빌드 시 파일이름 처리에 문제가 있는게 아닌가 싶다. "라이브러리.jar" 형태로 파일을 포함하여 빌드할 경우 최종 프로젝트 빌드파일에 추가된 이름이 "라이브러리-1.0.1.jar" 형태가 되는 것으로 보아 빌드과정에서 메이븐이 파일 이름을 변경하는 무언가가 있고, 미리 하이픈이 들어가 있을 경우 해당 파일 처리에 문제가 있는 것이 아닌가 싶다.

요약: 라이브러리 파일이름이 "라이브러리-1.0.1.jar"의 형태일 경우 하이픈을 포함한 버젼을 지워보자

코테에서 자주 쓰이는 알고리즘 기본형 With GPT

DFS

import java.util.*;

public class DFS {
    public static void dfs(int v, boolean[] visited, List<List<Integer>> graph) {
        visited[v] = true;
        System.out.print(v + " ");
        for (int i : graph.get(v)) {
            if (!visited[i]) {
                dfs(i, visited, graph);
            }
        }
    }

    public static void main(String[] args) {
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            graph.add(new ArrayList<>());
        }
        graph.get(0).add(1);
        graph.get(0).add(2);
        graph.get(1).add(0);
        graph.get(1).add(3);
        graph.get(2).add(0);
        graph.get(2).add(4);
        graph.get(3).add(1);
        graph.get(4).add(2);

        boolean[] visited = new boolean[5];
        dfs(0, visited, graph);
    }
}

BFS

import java.util.*;

public class BFS {
    public static void bfs(int start, List<List<Integer>> graph) {
        boolean[] visited = new boolean[graph.size()];
        Queue<Integer> queue = new LinkedList<>();
        queue.add(start);
        visited[start] = true;

        while (!queue.isEmpty()) {
            int v = queue.poll();
            System.out.print(v + " ");
            for (int i : graph.get(v)) {
                if (!visited[i]) {
                    visited[i] = true;
                    queue.add(i);
                }
            }
        }
    }

    public static void main(String[] args) {
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            graph.add(new ArrayList<>());
        }
        graph.get(0).add(1);
        graph.get(0).add(2);
        graph.get(1).add(0);
        graph.get(1).add(3);
        graph.get(2).add(0);
        graph.get(2).add(4);
        graph.get(3).add(1);
        graph.get(4).add(2);

        bfs(0, graph);
    }
}

다익스트라

import java.util.*;

public class Dijkstra {
    static class Node implements Comparable<Node> {
        int vertex, weight;
        Node(int v, int w) {
            this.vertex = v;
            this.weight = w;
        }

        public int compareTo(Node other) {
            return this.weight - other.weight;
        }
    }

    public static void dijkstra(int start, List<List<Node>> graph) {
        int n = graph.size();
        int[] dist = new int[n];
        Arrays.fill(dist, Integer.MAX_VALUE);
        dist[start] = 0;

        PriorityQueue<Node> pq = new PriorityQueue<>();
        pq.add(new Node(start, 0));

        while (!pq.isEmpty()) {
            Node node = pq.poll();
            int v = node.vertex;
            int w = node.weight;

            for (Node neighbor : graph.get(v)) {
                int newDist = w + neighbor.weight;
                if (newDist < dist[neighbor.vertex]) {
                    dist[neighbor.vertex] = newDist;
                    pq.add(new Node(neighbor.vertex, newDist));
                }
            }
        }

        System.out.println(Arrays.toString(dist));
    }

    public static void main(String[] args) {
        List<List<Node>> graph = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            graph.add(new ArrayList<>());
        }
        graph.get(0).add(new Node(1, 10));
        graph.get(0).add(new Node(2, 3));
        graph.get(1).add(new Node(2, 1));
        graph.get(1).add(new Node(3, 2));
        graph.get(2).add(new Node(3, 8));
        graph.get(3).add(new Node(4, 7));

        dijkstra(0, graph);
    }
}

플로이드워셜

import java.util.*;

public class FloydWarshall {
    public static void floydWarshall(int[][] graph) {
        int n = graph.length;
        int[][] dist = new int[n][n];

        for (int i = 0; i < n; i++) {
            System.arraycopy(graph[i], 0, dist[i], 0, n);
        }

        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (dist[i][k] + dist[k][j] < dist[i][j]) {
                        dist[i][j] = dist[i][k] + dist[k][j];
                    }
                }
            }
        }

        for (int[] row : dist) {
            System.out.println(Arrays.toString(row));
        }
    }

    public static void main(String[] args) {
        int INF = 99999;
        int[][] graph = {
            {0, 3, INF, 5},
            {2, 0, INF, 4},
            {INF, 1, 0, INF},
            {INF, INF, 2, 0}
        };

        floydWarshall(graph);
    }
}

벨만포드

import java.util.*;

public class BellmanFord {
    public static void bellmanFord(int[][] edges, int V, int start) {
        int[] dist = new int[V];
        Arrays.fill(dist, Integer.MAX_VALUE);
        dist[start] = 0;

        for (int i = 0; i < V - 1; i++) {
            for (int[] edge : edges) {
                int u = edge[0], v = edge[1], w = edge[2];
                if (dist[u] != Integer.MAX_VALUE && dist[u] + w < dist[v]) {
                    dist[v] = dist[u] + w;
                }
            }
        }

        for (int[] edge : edges) {
            int u = edge[0], v = edge[1], w = edge[2];
            if (dist[u] != Integer.MAX_VALUE && dist[u] + w < dist[v]) {
                System.out.println("Negative weight cycle detected");
                return;
            }
        }

        System.out.println(Arrays.toString(dist));
    }

    public static void main(String[] args) {
        int V = 5;
        int[][] edges = {
            {0, 1, -1}, {0, 2, 4},
            {1, 2, 3}, {1, 3, 2}, {1, 4, 2},
            {3, 2, 5}, {3, 1, 1}, {4, 3, -3}
        };

        bellmanFord(edges, V, 0);
    }
}

개인 포트폴리오용 서버를 Docker + Nginx + Vue + SpringBoot 환경으로 배포를 시도하고 있는 중이다.
수많은 문제점들과 SSL 인증서까지 다 적용에 간신히 성공했다. 수많은 트러블 중 만났던 하나의 트러블에 대한 해결방법을 남긴다.

 

SSL 인증서를 발급 받은 이후 docker nginx 에서 도메인으로 들어오는 http(80포트) 에 대해 https(443포트)로 리다이렉트 하도록 설정해 두었다. Frontend -> Backend API 요청은 문제없이 리다이렉트 되어서 해결되는 듯 하였지만 스웨거 페이지가 문제였다.

 

어째서인지 Swagger UI 에서의 요청은 Spring 에 닿지조차 못하였다. 그러던 중 요청시 https 페이지임에도 불구하고 curl 명령어가 http로 뜨는것을 확인했다. Swagger UI 에서는 curl 명령어로 실행해서 요청을 호출하는 것 같았다. 해당 명령어를 실행한 결과 Spring이 아니라 nginx 에서 Redirect 했다는 내용이 나오는 것을 확인했다.

 

아마 Swagger에서의 요청은 리다이렉트된 Spring 까지 도달하지 못하였던듯 하다. 관련해서 찾아보다 설정으로 처리 하는 방법을 발견하여 적용하였고 해결하였다.

 

server.forward-headers-strategy=framework

 

위와같은 설정을 application.properties로 추가하니 요청이 정상적으로 도달하였다.
해당 설정을 넣기전에는 스웨거의 curl 명령어가 https 라는 헤더정보가 누락된었던 듯 하며, 해당 설정 이후 정보들이 제대로 전달되어, swagger 의 curl 요청이 정상적으로 변경된듯 하다

Minikube

# minikube 상태확인  
minikube status  

# minikube 클러스터 삭제
minikube delete

# minikube 클러스터 생성
minikube start --driver=${가상머신}  

# minikube 서비스 접근
minikube service ${deployment-name}

Kubectl

# deployment 생성, 가상머신이므로 로컬 image가 아닌 hub repository 이용
kubectl create deployment ${deployment-name} --image=${remote-image} 

# deployment 제거
kubectl delete deployment ${deployment-name}

# deployment check
kubectl get deployments

# pod check
kubectl get pods

# LoadBalancer 이용하여 port 노출 및 service 생성  
kubectl expose deployment ${deployment-name} --type=LoadBalancer  --port=8080

# service check
kubectl get services

# service 접근  
minikube service ${deployment-name}  

# 다중 컨테이너 pods 실행  
kubectl scale deployment/${deplyment-name} --replicas=3

# 기존 deployment 컨테이너 새로운 이미지 설정  
kubectl set images deployment/${deployment-name} ${before-image-name}=${new-image-docker-hub-repository}

# deployment update 확인  
kubectl rollout status deployment/${deployment-name}  

# deployment history  
kubectl rollout history deployment/${deployment-name}  
kubectl rollout history deployment/${deployment-name} --revision=${revision}  

# deployment rollback  
# 이전  
kubectl rollout undo deployment/${deployment-name}  
# 특정 revision
kubectl rollout undo deployment/${deployment-name} --to-revision=${revision}  

# service 제거  
# kubectl delete service ${deployment-name}

# deployment 제거  
kubectl delete deployment ${deployment-name}

 

선언형 파일 이용
```shell
kubectl apply -f ${file-name}.yaml
```

# 선언형 파일 적용
kubectl apply -f ${file-name}.yaml

 

'Notes > Kubernetes' 카테고리의 다른 글

Window, WSL kubectl config 공유하기  (0) 2024.07.27

Window 에서 kubectl 을 설치 후 WSL 에서 클러스터 사용을 공유하기 위해 config 파일을 공유하는 방법


# wsl Linux 에 kubectl 설치
# https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

# kubectl 실행 권한 설정
chmod +x /kubectl

# 설정파일 저장 디렉토리 생성
mkdir -p ~/.kube

# 윈도우 설정파일 링크
ln -sf "/mnt/c/users/$windowsUser/.kube/config" ~/.kube/config

'Notes > Kubernetes' 카테고리의 다른 글

쿠버네티스 명령어(kubernetes command)  (0) 2024.07.27

+ Recent posts