STS 버리고 VS code로 Spring 디버깅 환경 구축하기

JaruKim·2021년 7월 29일
3
post-thumbnail

배경

저는 STS로 개발을 시작했기 때문에 STS에 모든 프로젝트들을 모아놓고 작업을 했습니다. 하나의 workspace에 모든 프로젝트들을 넣어두고 사용하는 프로젝트만 열어서 개발하는 방식이었습니다. 그런데 어느날 STS를 실행했는데 갑자기 에러가 발생했습니다. 그리고 workspace와 연결된 프로젝트 설정이 모두 날아갔습니다.

실제 프로젝트 폴더와 파일들이 없어지진 않았지만 프로젝트의 수가 꽤 되었기 때문에 workspace를 정리하고 프로젝트들을 복구하는데 많은 시간을 들여야했습니다. 그리고 크기가 큰 프로젝트를 다룰 때 자주 STS가 멈췄기 때문에 언젠가는 VS code로 바꿀 생각이었습니다. 마침내 때가 온 것이죠.

VS Code로 spring을 디버깅할 때의 문제점

프로젝트를 옮기는 건 어려운 일이 아니었습니다. 작업공간을 구성하고 폴더를 추가하면 끝이니까요. 물론 extension을 설치하고 설정하는게 귀찮기는 합니다. 문제는 디버깅이었습니다.

Spring boot는 extension에서 디버깅 기능을 지원합니다. 하지만 boot가 아닌 spring framework에서는 STS와 같은 디버깅을 VS code에서 할 수 없었습니다. Spring으로 개발한 웹 앱 프로젝트는 서버를 구동해서 디버깅을 해야하는데 별도의 extension이 존재하지 않기 때문입니다.

검색을 해보면 대부분 Tomcat for Java를 설치하라고 권장하지만 이 extension은 좀 문제가 많습니다.

  • 간혹 동작을 하지 않음.
  • Java 파일만 Hot code replacement가 가능함.
  • 디버깅을 하면서 static 파일들을 수정하기 번거로움
  • 장기간 업데이트가 없음.

Hot code replacement 기능은 디버깅을 하는 도중에 수정한 파일들을 즉시 업데이트하는 기능입니다. STS에서도 똑같은 기능을 지원하지만 큰 프로젝트를 다루면 종종 툴이 멈추는 원인이기 때문에 수동으로 빌드하고 했습니다.

어쨌든 Tomcat for Java는 오직 Java 파일만 가능하고 js나 jsp를 수정하기 위해선 직접 deploy된 경로에 가서 해당 파일을 열어서 수정해야 합니다. 아니면 디버깅을 새로 할 때마다 war 파일을 다시 만들어서 deploy를 해야합니다. 너무 번거롭죠.

그래서 boot가 아닌 Spring framework도 boot처럼 디버깅할 수 없을까라고 생각하게 되었습니다.

maven plugin으로 디버깅 세션 연결하기

VS code는 외부 디버거와 연결할 수 있습니다. 그러기 위해선 launch.json이라는 파일을 작성해서 설정해야 합니다.

launch.json.vscode 폴더 안에 위치합니다. 폴더가 없다면 VS code의 디버깅 사이드 메뉴를 열어서 launch.json을 작성할 수 있습니다.

작업 순서는 다음과 같습니다.
1. launch.json 작성.
2. tasks.json 작성.
3. pom.xml에 maven plugin 설정 추가.

launch.json 작성

아래의 코드를 참고하여 launch.json을 작성하면 됩니다. launch.json은 같은 디렉토리에 있는 tasks.json을 참조하여 preLaunchTaskpostDebugTask를 수행합니다.

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java", // 디버깅의 타입을 정의.
            "name": "Test Debug", // 디버깅 설정 목록에서 표시할 이름.
            "request": "attach", // 디버깅의 요청 형식을 정의. 'launch' 또는 'attach'.
            "hostName": "localhost", // 원격 디버거(여기선 메이븐)의 호스트 네임.
            "port": 8000, // 원격 디버거의 포트.
            "preLaunchTask": "run-tomcat", // 디버깅 세션이 실행되기 전에 수행될 task.
            "postDebugTask": "stop-tomcat", // 디버깅 세션 종료 후 수행될 task.
        }
    ]
}

tasks.json 작성

이 파일을 작성할 때의 주의점은 반드시 파일명을 tasks로 해야 합니다. 그리고 launch.json과 같은 디렉토리인 .vscode 폴더에 위치해야 합니다.

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "run-tomcat", // launch.json에서 사용할 task 이름.
            "type": "shell", // task의 타입.
            "command": "./mvnDebug -f \"./pom.xml\" -s \"./settings.xml\" tomcat7:run", // task로 실행할 명령어.
            "group": "build", // task를 할당할 그룹. 'build' 또는 'test' 이다.
            "isBackground": true, // task를 백그라운드에서 실행할 것인지에 대한 설정.
            "problemMatcher": [{ // 문제를 찾기위한 설정.
                "pattern": [{
                    "regexp": "\\b\\B",
                    "file": 1,
                    "location": 2,
                    "message": 3
                }],
                "background": {
                    "activeOnStart": true,
                    "beginsPattern": "^.*Listening for",
                    "endsPattern": "^.*transport dt_socket at address.*"
                }
            }]
        },
        {
            "label": "stop-tomcat",
            "type": "shell",
            "command": "echo ${input:terminate}}", // 하단 inputs 오브젝트 참고.
            "problemMatcher": []
        }
    ],
    "inputs": [
        {
            "id": "terminate",
            "type": "command",
            "command": "workbench.action.tasks.terminate",
            "args": "run-tomcat"
        }
    ]
}

tasks.json 참고 링크: [Github] Microsoft / vscode-java-debug

여기서 command 부분이 중요한데, 디버깅하려는 프로젝트의 pom.xml을 옵션으로 넣어줘야 합니다. 당연히 maven도 설치되어 있어야 합니다. mvnDebug는 아파치 메이븐에 포함되어 있는 프로그램입니다.

pom.xml에 maven plugin 설정 추가

pom.xml 파일을 열어보면 하단에 <build>라는 태그가 있고 그 아래에 plugin들을 기술합니다.

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.tomcat.maven</groupId>
			<artifactId>tomcat7-maven-plugin</artifactId>
			<version>2.2</version>
			<configuration>
				<server>Tomcat</server>
				<path>/</path>
				<charset>UTF-8</charset>
				<uriEncoding>UTF-8</uriEncoding>
			</configuration>
			<dependencies>
				<dependency>
					<groupId>org.apache.tomcat.embed</groupId>
					<artifactId>tomcat-embed-core</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-util</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-coyote</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-api</artifactId>
					<version>7.0.109</version>
				</dependency>
		  		<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-jdbc</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-dbcp</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-servlet-api</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-jsp-api</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-jasper</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-jasper-el</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-el-api</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-catalina</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-tribes</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-catalina-ha</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-annotations-api</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat</groupId>
					<artifactId>tomcat-juli</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat.embed</groupId>
					<artifactId>tomcat-embed-logging-juli</artifactId>
					<version>7.0.109</version>
				</dependency>
				<dependency>
					<groupId>org.apache.tomcat.embed</groupId>
					<artifactId>tomcat-embed-logging-log4j</artifactId>
					<version>7.0.109</version>
				</dependency>
			</dependencies>
		</plugin>
	</plugins>
</build>

tomcat 버전들을 특정한 이유는 java9 환경에서 일부 jar를 사용할 수 없는 에러가 발생하기 때문입니다. 오류 로그를 보면 아래와 같은 메시지를 확인할 수 있습니다.

Invalid byte tag in constant pool: 19

최신 버전에서는 해당 오류가 해결되었기 때문에 tomcat7의 최신 버전을 사용했습니다.

tomcat 버그 참고 링크: https://bz.apache.org/bugzilla/show_bug.cgi?id=60688

정리

tasks.json을 보면, run-tomcatmvnDebug tomcat:run를 실행하고 maven이 pom.xml의 톰캣 플러그인을 디버깅 모드로 실행하게 됩니다. 이때 기본 포트로 8000번이 설정되는데 포트를 변경하고 싶다면 mvnDebug 파일을 열어서 값을 수정해야합니다.

launch.json이 mvnDebug의 hostname과 port로 vscode 디버거를 연결합니다.

마무리

설정이 다 됐다면 VS code의 디버깅 사이드 메뉴에서 launch.json에서 설정한 이름을 찾고 디버깅을 실행합니다. 이틀 동안의 삽질이 빛을 보는 순간이었습니다.

0개의 댓글