오늘은 “톰캣을 설치해서 돌리는 방식” 말고, jar만 가져와서 내 자바 프로젝트에서 main()으로 톰캣을 직접 띄우는 방식을 정리한다.
톰캣은 “독립 실행”만 있는 게 아니다
톰캣을 받으면 보통 두 흐름이 있다.
- 독립 실행 톰캣: 설치(압축 해제) 후 bin/startup 같은 걸로 서버를 직접 띄운다.
- 임베디드 톰캣: 톰캣이 서버 프로그램이 아니라, 내 앱 안에 라이브러리(jar)로 들어가서 main()에서 실행된다.
오늘 한 방식은 두 번째다.
즉 “톰캣을 따로 켜는 게 아니라”, 자바 프로그램이 톰캣을 생성해서 켜는 구조다.

저 부분을 다운 받은 후 앞축을 해제한다.
준비: lib/에 톰캣 jar들을 넣고 VS Code에 클래스패스로 올리기

그럼 이렇게 jar파일들이 보인다.
새 프로젝트를 만든 뒤 lib/ 폴더를 만들고, 다운받은 톰캣 관련 jar들을 전부 복사해 넣는다.

VS Code(Java 확장)에서는 settings.json에 아래를 추가한다.
{
"java.project.referencedLibraries": ["lib/**/*.jar"]
}
이 의미는 단순하다.
`lib/` 아래의 모든 `jar`를 프로젝트 클래스패스에 자동으로 추가한다.
그래서 컴파일/실행/자동완성에서 Tomcat 클래스 같은 게 바로 잡힌다.
main()으로 톰캣 띄우는 런처 코드
package net.slipp;
import java.io.File;
import java.util.logging.Logger;
import org.apache.catalina.startup.Tomcat;
public class WebserverLauncher {
private static final Logger logger = Logger.getLogger(WebserverLauncher.class.getName());
public static void main(String[] args) throws Exception {
String webappDirLocation = "webapp/";
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.getConnector().setURIEncoding("UTF-8");
tomcat.getServer().setPort(8005);
tomcat.addWebapp("", new File(webappDirLocation).getAbsolutePath());
logger.info("configuring app with basedir: " + new File("./" + webappDirLocation).getAbsolutePath());
tomcat.start();
tomcat.getServer().await();
}
}
여기서 핵심 라인을 하나씩 뜯어보겠다.
`Tomcat tomcat = new Tomcat();`
임베디드 톰캣 “엔진 객체”를 만든다.
이 시점엔 아직 서버가 뜬 게 아니다. 그냥 설정할 대상이 생긴 거다.
`tomcat.setPort(8080);`
이건 브라우저가 접속하는 HTTP 포트다.
- 접속 URL: http://localhost:8080
여기서 8080은 “웹 접속 포트”다.
`tomcat.addWebapp("", webappDirAbsPath);`
이 라인이 “웹 루트”를 지정한다.
- 첫 번째 인자 ""는 컨텍스트 경로다.
- ""이면 루트 컨텍스트가 된다 → 즉 /로 붙는다.
- 두 번째 인자는 실제 정적 파일들이 있는 폴더 경로다.
- 여기서는 webapp/ 폴더.
즉 이 설정을 하면:
- http://localhost:8080/ → webapp/ 폴더를 루트로 보는 서버가 된다.
`tomcat.start();`
여기서 톰캣이 실제로 뜬다.
즉, 포트를 열고 서버 스레드들이 준비된다.
`tomcat.getServer().await();`
이게 “프로세스를 살아있게 하는 대기”다.
임베디드 톰캣은 내 프로그램이 주인이다.
내 main()이 끝나면 JVM이 종료되고, JVM이 종료되면 서버도 같이 죽는다.
그래서 await()로 메인 스레드를 붙잡아 두는 것이다.
그런데 왜 “바로 꺼져서 8080이 안 떴나?
현상은 이런 느낌이다.
- Run 누름
- 콘솔 조금 뜨는 듯하다가
- 프로세스가 종료됨
- `http://localhost:8080` 접속 안 됨
이건 대부분 이런 구조 때문에 생긴다.
- main()이 끝나면 JVM이 종료된다
- JVM이 종료되면 서버도 같이 내려간다
- 그런데 await()가 어떤 이유로 제대로 대기 상태로 들어가지 못하고 리턴해버리면
→ main()이 끝난다 → JVM 종료 → 서버 종료
즉, 문제는 톰캣이 아예 안 켜진 게 아니라
켜졌다가 main()이 끝나서 같이 죽은 케이스가 많다.
tomcat.getServer().setPort(8005); 이건 뭐고 왜 필요한가
웹 접속 포트는 8080 그대로다.
- tomcat.setPort(8080); → HTTP 접속 포트
- tomcat.getServer().setPort(8005); → Tomcat 서버 내부의 shutdown/await 포트
1) 8005는 브라우저랑 관계가 없다
브라우저로 `http://localhost:8005` 이런 식으로 접속하는 게 아니다.
이 포트는 Tomcat 내부에서 서버를 기다리는(await)/종료 신호 같은 관리용으로 쓰이는 영역이다.
2) 이걸 설정하면 뭐가 좋아지나
핵심 효과는 하나다.
- tomcat.getServer().await();가 진짜로 블로킹(대기) 해서
- main()이 끝나지 않고
- JVM이 살아 있고
- 따라서 서버가 계속 살아 있다.
즉, “서버가 켜졌다가 바로 꺼지는 문제”를 막는 프로세스 유지용 안전장치다.
한글 깨짐
setURIEncoding("UTF-8")은 말 그대로 URI 인코딩이다.
즉 이런 상황에서 의미가 있다.
- GET 쿼리스트링에 한글이 들어갈 때
예: /?name=홍길동 - 경로에 한글이 들어갈 때
이때 서버가 URI를 해석할 때 UTF-8로 디코딩하게 만든다.
정적 HTML이 깨지는 건 보통 두 가지가 원인이다.
- 파일 자체가 UTF-8로 저장되지 않았다
- HTML에 charset 선언이 없다(또는 다르다)
그래서 해결은 이쪽이다.
- 파일을 UTF-8로 저장
- HTML에 선언 추가
즉,
- setURIEncoding("UTF-8") = URL/파라미터 쪽
- meta charset + 파일 저장 인코딩 = HTML 본문 표시 쪽
둘은 대상이 다르다.
index.html이 “디폴트로 뜨는 이유”
이건 톰캣(그리고 대부분의 웹서버)의 기본 규칙이다.
- 사용자가 /로 요청하면 디렉토리 루트를 의미한다.
- 디렉토리 요청에서 파일명을 생략한 경우 서버는 기본 파일을 찾는다.
- 대표 기본 파일이 index.html이다.
즉,
- `http://localhost:8080/ `요청
- `webapp/` 폴더가 루트
- `webapp/index.html`이 있으면 그걸 자동으로 반환
내가 설정을 한 게 아니라, 서버가 가진 기본 동작이다.
정적 HTML 수정은 서버 재시작 없이 반영되나?
대부분은 서버 재시작 없이 브라우저 새로고침만 하면 반영된다.
특히 지금 구조가
- 임베디드 톰캣
- webapp/를 디렉토리로 붙여서 제공
이런 형태라면, 파일 변경이 바로 반영되는 경우가 많다.
다만 안 바뀌는 것처럼 보이면 대부분 브라우저 캐시다.
- 강력 새로고침: (mac) Cmd + Shift + R
체크 방법: 진짜로 8080이 열렸는지 확인
서버가 살아있는지 확인할 때는 아래가 확실하다.
- 브라우저 접속: http://localhost:8080
- 포트 점유 확인(맥):
여기서 java 프로세스가 잡히면 정상이다.
출처 : 《자바 웹 프로그래밍 Next Step》, 박재성, 로드북
'Book > 자바 웹 프로그래밍 Next Step' 카테고리의 다른 글
| 5장_ : 서블릿 컨테이너가 하는 일: 생명주기와 인스턴스 생성 (0) | 2026.02.05 |
|---|---|
| 5장_ : Servlet으로 Hello World 출력하기 (0) | 2026.02.05 |
| 3장_ 로깅: System.out.println() 대신 로깅을 써야 하는 이유 (0) | 2026.02.02 |
| 3장_ 요구사항 4: 302 status code 적용 (0) | 2026.02.01 |
| 3장_ 요구사항3: POST방식으로 회원가입 (0) | 2026.02.01 |
