i18n
i18n은 국제화 소프트웨어를 다양한 언어와 문화권에서 쉽게 지역화할 수 있도록 준비하는 과정입니다 코드와 데이터의 분리, 문자열 리소스 파일 사용, 유니코드 지원 등 다양한 기술적 준비를 포함합니다.
우리는 이번에 HTTP 메시지를 만들어보고 이들을 국제화 작업을 한번 해보려고 합니다
StudentController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@RestController
@RequestMapping(value ="v1/school/{schoolId}/student")
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/{studentId}")
public ResponseEntity<Student> getStudent(
@PathVariable("schoolId") String schoolId ,
@PathVariable("studentId") String studentId
)
{
return new ResponseEntity<>(studentService.getStudent(studentId , schoolId) , null , HttpStatus.OK);
}
@PostMapping
public ResponseEntity<String> createStudent(
@PathVariable("schoolId") String schoolId ,
@RequestBody Student student
)
{
return new ResponseEntity<>(studentService.createStudent(student , schoolId) , null , HttpStatus.OK);
}
@PutMapping
public ResponseEntity<String> updateStudent (
@PathVariable("schoolId") String schoolId ,
@RequestBody Student student
)
{
return new ResponseEntity<>(studentService.updateStudent(student,schoolId) , null , HttpStatus.OK);
}
@DeleteMapping(value = "/{studentId}")
public ResponseEntity<String> deleteStudent(
@PathVariable("schoolId") String schoolId ,
@PathVariable("studentId") String studentId
)
{
return new ResponseEntity<>(studentService.deleteStudent(studentId , schoolId) , null , HttpStatus.OK);
}
}
학생 정보를 가져오고 입력하고 수정하고 삭제하고 업데이트하는 엔드 포인트입니다 Student POJO 클래스는 다음과 같습니다
Student
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
@Setter
@ToString
public class Student {
private int id;
private String studentId;
private String studentName;
private String schoolId;
private String gender;
private String higherGroup;
}
StudentService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Service
public class StudentService {
public Student getStudent(String studentId , String schoolId){
Student student = new Student();
student.setId(new Random().nextInt(5000));
student.setStudentId(studentId);
student.setSchoolId(schoolId);
student.setStudentName("Kim");
student.setGender((new Random().nextInt(2) + 1) == 1 ? "Man" : "Woman");
student.setHigherGroup((new Random().nextInt(2) + 1) == 1 ? "Y" : "N");
return student;
}
public String createStudent(Student student , String schoolId)
{
String responseMessage = null;
if(student != null){
student.setSchoolId(schoolId);
responseMessage = String.format("학생입력이 정상적으로 되었습니다 입력된 학생의 정보는 : %s" , student.toString());
}
return responseMessage;
}
public String updateStudent(Student student , String schoolId)
{
String responseMessage = null;
if(student != null){
student.setSchoolId(schoolId);
responseMessage = String.format("학생 업데이트 정상적으로 되었습니다 수정된 학생의 정보는 : %s" , student.toString());
}
return responseMessage;
}
public String deleteStudent(String studentId , String schoolId)
{
String responseMessage = null;
responseMessage = String.format("학생 번호 %s 는 학교 번호 %s 로 부터 삭제 되었습니다" , studentId , schoolId);
return responseMessage;
}
}
만약 해당 서비스를 한국어에 국한된 서비스라면 굳이 국제화 작업을 할 필요 없습니다 국제화 작업도 꽤나 노동이 들어가기 때문입니다 하지만 우리는 전 세계를 위한 작업을 해야 하므로 국제화 작업을 진행을 하겠습니다 현재 보이는 controller 와 서비스는 HttpMessage로 한글을 주고 있습니다 이런 식으로 하드코딩을 하게 되면 국제화 작업이 가능하지 않습니다 해당 언어를 위한 서비스를 언어가 추가될 때마다 작업을 해야 하는데 그렇게 할 수 없습니다 그래서 Spring MessageSource를 사용해서 손쉽게 국제화를 진행을 할 수 있습니다
MessageSource 빈 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@SpringBootApplication
public class ProjectSpringCloudApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectSpringCloudApplication.class, args);
}
@Bean
public LocaleResolver localeResolver(){
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.KOREA);
return localeResolver;
}
@Bean
public ResourceBundleMessageSource resourceBundleMessageSource(){
ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
resourceBundleMessageSource.setUseCodeAsDefaultMessage(true);
resourceBundleMessageSource.setDefaultEncoding("UTF-8");
resourceBundleMessageSource.setBasenames("messages");
return resourceBundleMessageSource;
}
}
2개의 bean 을 정의하겠습니다 LocaleResolver는 기본으로 사용할 언어의 기본입니다 뒤에 헤더에 특정 나라를 구분하는 코드가 들어오지 않으면 기본적으로 KR 을 제공하겠다는 뜻입니다 ResourceBundleMessageSource는 properties의 어떤 파일을 기본적으로 메시지를 읽는지와 인코딩 설정을 할 수 있습니다
messageSoruce 만들기
messages.propreteis
1
2
3
4
student.create.message = 학생 입력이 정상적으로 되었습니다 입력된 학생의 정보는 : %s -default_model
student.update.message = 학생 정보수정이 정상적으로 되었습니다 수정된 학생의 정보는 : %s -default_model
student.delete.message = 학생 번호 %s 는 학교 번호 %s 로 부터 삭제 되었습니다 - default_model
messages_kr.propreteis
1
2
3
4
student.create.message = 학생 입력이 정상적으로 되었습니다 입력된 학생의 정보는 : %s -kr_model
student.update.message = 학생 정보수정이 정상적으로 되었습니다 수정된 학생의 정보는 : %s -kr_model
student.delete.message = 학생 번호 %s 는 학교 번호 %s 로 부터 삭제 되었습니다 - kr_model
messages_en.propreteis
1
2
3
4
student.create.message = Student input has been successfully received. The entered student information is: %s -en_model
student.update.message = Student information has been successfully updated. The updated student information is : %s -en_model
student.delete.message = StudentId %s and SchoolId %s was Deleted.- en_model
총 3가지 메시지 정보를 만들었다 기본적으로 특정 헤더에 국가 코드가 들어오지 않으면 message.propreteis 정보가 들어올 것이고 특정 국가 코드를 넣게 되면 해당 국제화 표준모델로 메시지가 나가게 됩니다 그럼 Controller 과 Serivce를 수정해야 합니다 이때 각 꼬리표는 확인을 위함입니다 국제화 모델하고는 상관이 없습니다
Controller 수정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@PostMapping
public ResponseEntity<String> createStudent(
@PathVariable("schoolId") String schoolId ,
@RequestBody Student student,
@RequestHeader(value = "Accept-Language" , required = false) Locale locale
)
{
return new ResponseEntity<>(studentService.createStudent(student , schoolId , locale) , null , HttpStatus.OK);
}
@PutMapping
public ResponseEntity<String> updateStudent (
@PathVariable("schoolId") String schoolId ,
@RequestBody Student student,
@RequestHeader(value = "Accept-Language" , required = false) Locale locale
)
{
return new ResponseEntity<>(studentService.updateStudent(student,schoolId ,locale) , null , HttpStatus.OK);
}
@DeleteMapping(value = "/{studentId}")
public ResponseEntity<String> deleteStudent(
@PathVariable("schoolId") String schoolId ,
@PathVariable("studentId") String studentId ,
@RequestHeader(value = "Accept-Language" , required = false) Locale locale
)
{
return new ResponseEntity<>(studentService.deleteStudent(studentId , schoolId , locale) , null , HttpStatus.OK);
}
3개의 핸들러를 수정할 것입니다 이때 RequestHeader에서 특정 국가 코드를 Accept-Language key 값으로 받아서 locale에 넣어줄 것입니다 그것을 service로 넘겨서 특정 국가 코드의 국제화 모듈을 실행합니다
Service 수정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Autowired
private MessageSource messageSource;
public String createStudent(Student student , String schoolId , Locale locale)
{
String responseMessage = null;
if(student != null){
student.setSchoolId(schoolId);
responseMessage = String.format(messageSource.getMessage("student.create.message" , null , locale) , student.toString());
}
return responseMessage;
}
public String updateStudent(Student student , String schoolId , Locale locale)
{
String responseMessage = null;
if(student != null){
student.setSchoolId(schoolId);
responseMessage = String.format(messageSource.getMessage("student.update.message" , null , locale) , student.toString());
}
return responseMessage;
}
public String deleteStudent(String studentId , String schoolId , Locale locale)
{
String responseMessage = null;
responseMessage = String.format(messageSource.getMessage("student.delete.message" , null , locale) , studentId , schoolId);
return responseMessage;
}
3개의 서비스 핸들러를 수정했습니다 MessageSource 의존성을 주입 후 해당 의존성으로 properties에 매핑된 각 메시지 정보를 가져오는데 이때 가져오는 정보는 locale에 담겨 있습니다
HTTP 요청정보 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /v1/school/1/student HTTP/1.1
Host: localhost:8080
Accept-Language: en
Content-Type: application/json
Content-Length: 162
{
"id": 600 ,
"studentId" : "2366",
"studentName" : "MI KO",
"schoolId" : "1" ,
"gender" : "woman",
"higherGroup" : "Y"
}
만약 이와 같이 Accept-Language: en으로 헤더를 날려주면 MessageSource는 국제화 모듈에 맞춘 messages_en.properties의 값을 가져오게 됩니다
1
Student input has been successfully received. The entered student information is: Student(id=600, studentId=2366, studentName=MI KO, schoolId=1, gender=woman, higherGroup=Y) -en_model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PUT /v1/school/1/student HTTP/1.1
Host: localhost:8080
Accept-Language: kr
Content-Type: application/json
Content-Length: 162
{
"id": 600 ,
"studentId" : "2366",
"studentName" : "MI KO",
"schoolId" : "2" ,
"gender" : "woman",
"higherGroup" : "Y"
}
학생 정보 수정이 정상적으로 되었습니다 수정된 학생의 정보는 : Student(id=600, studentId=2366, studentName=MI KO, schoolId=1, gender=woman, higherGroup=Y) -kr_model
kr 요청도 이와 같이 들어오게 되고 마지막 국제화를 만들어놓지 않은 경우는 default.messagesProperties를 향하게 됩니다
1
2
3
4
5
DELETE /v1/school/1/student/20?Accept-Language=UK HTTP/1.1
Host: localhost:8080
학생 번호 20 는 학교 번호 1 로 부터 삭제 되었습니다 - default_model
실제 UK 모델은 아직 만들지 않았기 때문에 지금처럼 default_model 출력이 됩니다 오늘은 하나의 애플리케이션에서 여러 국제화 모듈을 사용하는 i18n에 대해서 알아보았습니다