Spring Boot Thymeleaf 파비콘 설정 완전 가이드
Spring Boot + Thymeleaf 파비콘 설정 — 생성부터 트러블슈팅까지
프로젝트를 진행하다 보면 기능 개발에 집중하느라 파비콘 같은 기본 세팅을 빠뜨리는 경우가 종종 있다. 브라우저 탭에 기본 아이콘이 그대로 남아 있으면 완성도가 떨어져 보이니, 이번에 파비콘 설정 과정을 정리해둔다.
파비콘이란
브라우저 탭, 북마크 바, 모바일 홈 화면 등에 표시되는 작은 사이트 아이콘이다. 예전에는 favicon.ico 하나만 루트에 넣으면 끝이었지만, 지금은 다양한 디바이스와 해상도를 대응해야 하므로 여러 파일이 필요하다.
파비콘 생성
favicon.io에서 텍스트 기반이나 이미지 기반으로 생성할 수 있다. 생성 후 zip을 다운로드하면 다음 파일이 포함되어 있다.
favicon.ico— 레거시 브라우저용 (16×16, 32×32 멀티사이즈)favicon-16x16.png,favicon-32x32.png— 모던 브라우저 탭apple-touch-icon.png(180×180) — iOS 홈 화면android-chrome-192x192.png,android-chrome-512x512.png— Android, PWAsite.webmanifest— PWA manifest
파일 배치
Spring Boot 기준으로 src/main/resources/static/ 하위에 넣는다. 멀티 스토어처럼 파비콘을 분리해야 하는 경우 하위 디렉토리로 구분하면 된다.
src/main/resources/static/favicon/
├── wooriwon/
│ ├── favicon.ico
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── apple-touch-icon.png
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ └── site.webmanifest
└── onnuri/
└── (동일 구조)
HTML head 선언
Thymeleaf layout의 <head> 안에 link 태그를 추가한다. th:href="@{...}" 문법을 쓰면 context path가 /가 아닌 경우에도 경로가 자동 보정된다.
<link rel="icon" type="image/x-icon" th:href="@{/favicon/wooriwon/favicon.ico}">
<link rel="icon" type="image/png" sizes="32x32" th:href="@{/favicon/wooriwon/favicon-32x32.png}">
<link rel="icon" type="image/png" sizes="16x16" th:href="@{/favicon/wooriwon/favicon-16x16.png}">
<link rel="apple-touch-icon" sizes="180x180" th:href="@{/favicon/wooriwon/apple-touch-icon.png}">
<link rel="manifest" th:href="@{/favicon/wooriwon/site.webmanifest}">
멀티 스토어라면 Thymeleaf fragment로 분리해서 조건부로 include하는 패턴이 깔끔하다.
<th:block th:fragment="wooriwonFaviconFragment">
<!-- 위 link 태그들 -->
</th:block>
site.webmanifest 설정
favicon.io가 생성해주는 기본 manifest에서 icon 경로를 실제 배치 경로와 맞춰야 한다.
{
"name": "",
"short_name": "",
"icons": [
{ "src": "/favicon/wooriwon/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/favicon/wooriwon/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
Spring Security 허용 처리
anyRequest().permitAll()로 설정되어 있더라도, WebSecurity의 ignoring에 favicon 경로를 추가하는 것이 좋다. security filter chain 자체를 안 타므로 불필요한 오버헤드를 제거할 수 있다.
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/static/**", "/favicon/**");
}
트러블슈팅: Manifest Syntax Error
증상
Manifest: Line: 1, column: 1, Syntax error.
원인
JSON 문법이 아니라 파일 인코딩 문제인 경우가 대부분이다. 파일 앞에 UTF-8 BOM(Byte Order Mark, EF BB BF)이 붙어 있으면 JSON 파서가 첫 문자를 인식하지 못한다. Windows 메모장 등 일부 에디터에서 저장 시 자동으로 붙는다.
해결
VS Code 기준으로 하단 바의 인코딩 표시를 클릭하고, "Save with Encoding"에서 UTF-8 (BOM 없는 것)을 선택한다. 파일 첫 문자가 {인지도 확인한다 — 앞에 빈 줄이나 공백이 있어도 같은 에러가 난다.
캐싱 문제
파비콘은 브라우저가 매우 강하게 캐싱한다. 변경 후 반영이 안 되면 브라우저 캐시를 삭제하거나, 직접 파비콘 URL에 접속한 뒤 Ctrl+Shift+R로 hard refresh를 시도한다. 그래도 안 되면 link 태그에 쿼리 파라미터로 버전을 붙이는 방법도 있다.
<link rel="icon" ... th:href="@{/favicon/wooriwon/favicon-32x32.png?v=2}">
정리
파비콘 세팅 자체는 단순하지만, Spring Security 경로 허용을 빠뜨리거나 manifest 인코딩 문제를 모르면 시간을 낭비할 수 있다. 핵심은 세 단계다: static 하위에 파일 배치 → layout head에 link 태그 → Security ignoring 추가. favicon.io가 생성해주는 HTML snippet을 복사하되 href만 th:href="@{...}"로 바꾸면 된다.