Skip to content

Commit bdf2868

Browse files
committed
initial commit
0 parents  commit bdf2868

30 files changed

+1337
-0
lines changed

.gitignore

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
HELP.md
2+
.gradle
3+
build/
4+
!gradle/wrapper/gradle-wrapper.jar
5+
!**/src/main/**/build/
6+
!**/src/test/**/build/
7+
8+
### STS ###
9+
.apt_generated
10+
.classpath
11+
.factorypath
12+
.project
13+
.settings
14+
.springBeans
15+
.sts4-cache
16+
bin/
17+
!**/src/main/**/bin/
18+
!**/src/test/**/bin/
19+
20+
### IntelliJ IDEA ###
21+
.idea
22+
*.iws
23+
*.iml
24+
*.ipr
25+
out/
26+
!**/src/main/**/out/
27+
!**/src/test/**/out/
28+
29+
### NetBeans ###
30+
/nbproject/private/
31+
/nbbuild/
32+
/dist/
33+
/nbdist/
34+
/.nb-gradle/
35+
36+
### VS Code ###
37+
.vscode/

README.md

+335
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# 기본 정보
2+
3+
- 언어 코드: ISO-639 두 자리 언어 코드를 기본으로 사용
4+
- 지원 확장자: `.properties`, `.xml`
5+
6+
# Locale 인식 우선순위
7+
8+
이 예시 프로젝트에서 다음 항목은 서버가 지원하는 언어 목록에 해당할 때만 반영됩니다.
9+
10+
1. lang 파라미터
11+
- 이후 세션과 쿠키에 저장됩니다.
12+
- 세션과 쿠키에 저장할 떄는 서버 지원 언어 목록이 아니더라도 입력된 대로 저장됩니다.
13+
2. 세션 "SESSION_LOCALE" (커스텀된 세션 속성 이름)
14+
3. 쿠키 "LOCALE_LANG" (커스텀된 쿠키 이름)
15+
4. Accept-Language 헤더
16+
- 주로 브라우저가 자동으로 붙여 주는 값입니다.
17+
- 사용자 상호작용으로 추가될 수 있는 lang, 세션, 쿠키에 비해 우선순위가 낮습니다.
18+
- 우선순위에 따라 서버가 지원하는 언어일 때만 이 헤더의 locale 언어를 사용합니다.
19+
5. 시스템 기본 값 (또는 애플리케이션 설정 기본값)
20+
- 이 예시 프로젝트는 시스템의 기본 값 대신 `Locale.ENGLISH`를 사용합니다.
21+
22+
# 사용자 입력 정보
23+
24+
## Lang 파라미터
25+
26+
사용자 요청에 `lang` 파라미터가 있을 때 locale이 명시적으로 변경됩니다.
27+
`LocaleChangeInterceptor``LocaleResolver` 빈에 의해 locale이 변경됩니다.
28+
29+
## 세션과 쿠키
30+
31+
(이 예시 프로젝트에서는) 사용자가 `lang` 파라미터를 보내면 그 정보를 세션과 쿠키에 저장합니다.
32+
사용자의 정보를 기억하는 것이기 때문에 서버가 지원하는 언어 코드가 아니더라도 기억합니다.
33+
하지만 서버가 지원하지 않는 언어 코드일 때는 기억된 내용도 사용자의 locale로 취급하지 않고 무시됩니다.
34+
35+
## Accept-Language Header (Browser Default)
36+
37+
브라우저가 주로 자동으로 붙여 주는 헤더입니다.
38+
항목을 콤마로 구분하고, 다음 예시에서 참고할 수 있는 `q` 값을 통해 우선순위를 구분합니다.
39+
40+
만약 `Accept-Language` 헤더가 `en-US,de;q=0.9,ko;q=0.8`라는 내용으로 도착한다면 우선순위는 다음 순서로 적용됩니다.
41+
42+
1. `en-US``q=1`로 임의 적용됩니다. `q`가 가장 높기 때문에 우선순위가 가장 높습니다.
43+
2. `de;q=0.9`는 독일어 언어 코드(`de`)로, `q=0.9`로 두 번째로 우선순위가 높습니다.
44+
3. `ko;q=0.8`은 한국어 언어 코드(`ko`)로, `q=0.8`로 세 번째로 우선순위가 높습니다.
45+
4. 작성 순서와 별개로 `q`의 대소 관계에 따라 로케일 우선순위를 결정합니다.
46+
(`en-US;q=0.7`이었다면, `de`, `ko`, `en-US`순으로 우선순위가 조정됩니다.)
47+
5. 이 예시 프로젝트는 서버에서 지원하는 언어 코드에 해당하는 것 중 우선순위가 가장 높은 언어를 locale 언어로 선택합니다.
48+
49+
예시에 있는 `en-US`는 언어 코드(`en`)와 국가 코드(`US`)가 있는 Locale입니다.
50+
`en-US` locale이 `en` locale과 호환되도록 코드를 작성합니다.
51+
52+
# 기타 정보
53+
54+
## 모듈의 구분
55+
56+
모듈의 구분에 대한 설명은
57+
[src/main/java/letsdev/demo/configuration/modular/example/README.md](
58+
./src/main/java/letsdev/demo/configuration/modular/example/README.md
59+
)에 있습니다.
60+
61+
## Validation 우선순위
62+
63+
한 필드에 대해 여러 validation field error가 발생하면 우선순위는 다음과 같습니다.
64+
`priority` 값이 낮을수록 높은 우선순위를 갖습니다.
65+
66+
```java
67+
private int getPriority(FieldError error) {
68+
return switch (error.getCode()) {
69+
case String s when s.startsWith("NotNull") -> 10;
70+
case String s when s.startsWith("NotEmpty") || s.startsWith("NotBlank") -> 20;
71+
case String s when s.startsWith("Size") -> 30;
72+
case String s when s.startsWith("Pattern") -> 40;
73+
case String s when s.startsWith("Email") -> 50;
74+
case String s when s.startsWith("Min") || s.startsWith("Max") -> 60;
75+
case String s when s.startsWith("Digits") -> 70;
76+
case String s when s.startsWith("Future") || s.startsWith("Past") -> 80;
77+
case String s when s.startsWith("Positive") || s.startsWith("Negative") -> 90;
78+
case String s when s.startsWith("AssertTrue") || s.startsWith("AssertFalse") -> 100;
79+
case null, default -> Integer.MAX_VALUE;
80+
};
81+
}
82+
```
83+
84+
예를 들어 `@NotBlank`, `@Size`, `@Pattern`에서 유효성이 맞지 않는다면 우선순위는 다음과 같습니다.
85+
86+
- `@NotBlank` 오류
87+
- `@Size` 오류
88+
- `@Pattern` 오류
89+
90+
## 일부만 번역된 파일
91+
92+
서버가 지원하는 언어 목록에 있는 locale이더라도 만약 일부 속성만 번역되었다면,
93+
번역되지 않은 속성들은 다음 우선순위 locale 언어가 아니라 기본값인 `en`이 적용됩니다.
94+
다국어 메시지 소스 리졸버(resolver)의 기본 동작 때문입니다.
95+
96+
# Example Request
97+
98+
## 기본 값
99+
100+
- 언어 코드를 작성하지 않았을 때 영어로 validation 메시지가 옵니다.
101+
- URL: http://localhost:8080/sign-up
102+
- Method: `POST`
103+
- Request Body (JSON)
104+
```json
105+
{
106+
"username": "",
107+
"password": "InputPassPhrase",
108+
"nickname": "John"
109+
}
110+
```
111+
- Response Body
112+
```json
113+
{
114+
"timestamp": "2024-10-01T00:00:00.000000Z",
115+
"status": 400,
116+
"error": "Bad Request",
117+
"message": "Please enter a username.",
118+
"path": "/sign-up"
119+
}
120+
```
121+
122+
## 언어별 lang 파라미터
123+
124+
지원 언어 목록
125+
126+
- `en`: 영어
127+
- `ko`: 한국어
128+
- `ja`: 일본어
129+
- `zh`: 중국어
130+
131+
요청 및 응답 예시
132+
133+
- `lang=ko` 파라미터를 담아서 보낸 요청은 validation 메시지가 한국어로 옵니다.
134+
- URL: http://localhost:8080/sign-up?lang=ko
135+
- Method: `POST`
136+
<details>
137+
<summary>Request Body (JSON)</summary>
138+
139+
```json
140+
{
141+
"username": "",
142+
"password": "InputPassPhrase",
143+
"nickname": "John"
144+
}
145+
```
146+
147+
</details>
148+
- Response Body (Example)
149+
150+
```json
151+
{
152+
"timestamp": "2024-10-01T00:00:00.000000Z",
153+
"status": 400,
154+
"error": "Bad Request",
155+
"message": "사용자 이름을 입력하세요.",
156+
"path": "/sign-up"
157+
}
158+
```
159+
160+
- `lang=fr`(지원 언어가 아님) 파라미터를 담아서 보낸 요청은 validation 메시지가 기본 언어인 영어로 옵니다.
161+
- URL: http://localhost:8080/sign-up?lang=ko
162+
- Method: `POST`
163+
<details>
164+
<summary>Request Body (JSON)</summary>
165+
166+
```json
167+
{
168+
"username": "",
169+
"password": "InputPassPhrase",
170+
"nickname": "John"
171+
}
172+
```
173+
174+
</details>
175+
- Response Body (Example)
176+
```json
177+
{
178+
"timestamp": "2024-10-01T00:00:00.000000Z",
179+
"status": 400,
180+
"error": "Bad Request",
181+
"message": "Please enter a username.",
182+
"path": "/sign-up"
183+
}
184+
```
185+
186+
### 세션 및 쿠키에 저장됨
187+
188+
[세션과 쿠키](#세션과-쿠키) 항목을 참고하세요.
189+
`lang` 파라미터로 전달한 내용은 세션이나 쿠키에 저장됩니다.
190+
갱신은 `lang` 파라미터로 새로운 언어 코드를 전달하면 됩니다.
191+
192+
지원 언어 목록
193+
194+
- `en`: 영어
195+
- `ko`: 한국어
196+
- `ja`: 일본어
197+
- `zh`: 중국어
198+
199+
## 언어 및 지역에 따른 Accept-Language 헤더와 우선순위
200+
201+
지원 언어 목록
202+
203+
- `en`: 영어
204+
- `ko`: 한국어
205+
- `ja`: 일본어
206+
- `zh`: 중국어
207+
208+
요청 및 응답 예시
209+
210+
- 첫 번째 항목이 언어 및 지역 코드, 두 번째 항목이 언어 코드이며 둘이 일치하는 것이 보통입니다.
211+
- URL: http://localhost:8080/sign-up?lang=ko
212+
- Method: `POST`
213+
- Accept-Language: `en-US,en;q=0.9,ko;q=0.8`
214+
<details>
215+
<summary>Request Body (JSON)</summary>
216+
217+
```json
218+
{
219+
"username": "",
220+
"password": "InputPassPhrase",
221+
"nickname": "John"
222+
}
223+
```
224+
225+
</details>
226+
- Response Body (Example)
227+
```json
228+
{
229+
"...": "",
230+
"message": "Please enter a username.",
231+
"...": ""
232+
}
233+
```
234+
235+
- 앞 목록이 지원 언어가 아니고 뒤 목록에 지원 언어가 있다면,
236+
지원언어 중 가장 우선순위가 높은 언어로 validation 메시지를 응답합니다.
237+
- URL: http://localhost:8080/sign-up?lang=ko
238+
- Method: `POST`
239+
- Accept-Language: `de-DE,de;q=0.9,ko;q=0.8`
240+
<details>
241+
<summary>Request Body (JSON)</summary>
242+
243+
```json
244+
{
245+
"username": "",
246+
"password": "InputPassPhrase",
247+
"nickname": "John"
248+
}
249+
```
250+
251+
</details>
252+
- Response Body (Example)
253+
```json
254+
{
255+
"...": "",
256+
"message": "사용자 이름을 입력하세요.",
257+
"...": ""
258+
}
259+
```
260+
261+
- 모든 목록이 지원 언어가 아니면, 기본 언어인 영어로 validation 메시지를 응답합니다.
262+
- URL: http://localhost:8080/sign-up?lang=ko
263+
- Method: `POST`
264+
- Accept-Language: `de-DE,de;q=0.9,fr;q=0.8`
265+
<details>
266+
<summary>Request Body (JSON)</summary>
267+
268+
```json
269+
{
270+
"username": "",
271+
"password": "InputPassPhrase",
272+
"nickname": "John"
273+
}
274+
```
275+
276+
</details>
277+
- Response Body (Example)
278+
```json
279+
{
280+
"...": "",
281+
"message": "Please enter a username.",
282+
"...": ""
283+
}
284+
```
285+
286+
- 첫 번째 항목이 지원 언어지만 국가 코드를 포함할 때도 첫 번째 항목의 언어를 잘 파싱하여 validation 메시지를 응답합니다.
287+
- URL: http://localhost:8080/sign-up?lang=ko
288+
- Method: `POST`
289+
- Accept-Language: `en-US,de;q=0.9,ko;q=0.8`
290+
<details>
291+
<summary>Request Body (JSON)</summary>
292+
293+
```json
294+
{
295+
"username": "",
296+
"password": "InputPassPhrase",
297+
"nickname": "John"
298+
}
299+
```
300+
301+
</details>
302+
- Response Body (Example)
303+
```json
304+
{
305+
"...": "",
306+
"message": "Please enter a username.",
307+
"...": ""
308+
}
309+
```
310+
311+
- 정렬되어 있지 않아도 우선순위를 잘 해석하여,
312+
서버가 지원하는 언어 중 우선순위가 가장 높은 언어로 validation 메시지를 응답합니다.
313+
- URL: http://localhost:8080/sign-up?lang=ko
314+
- Method: `POST`
315+
- Accept-Language: `en-US;q=0.7,de;q=0.9,ko;q=0.8`
316+
<details>
317+
<summary>Request Body (JSON)</summary>
318+
319+
```json
320+
{
321+
"username": "",
322+
"password": "InputPassPhrase",
323+
"nickname": "John"
324+
}
325+
```
326+
327+
</details>
328+
- Response Body (Example)
329+
```json
330+
{
331+
"...": "",
332+
"message": "사용자 이름을 입력하세요.",
333+
"...": ""
334+
}
335+
```

0 commit comments

Comments
 (0)