벡엔드 웹프로그래밍/JSP

JSP 웹프로그래밍 63일차 (25/2/14)

wkun 2025. 2. 14. 17:59

contents

 

PART 03 페이지 처리 기법

chapter 08 유효성 검사: 도서 등록 데이터의 유효성 검사하기

01.유효성 검사의 개요

02.기본 유효성 검사

03.데이터 형식 유효성 검사

04.북마켓 도서 등록 데이터의 유효성 검사하기

chapter 09  다국어 처리: 도서 등록 페이지의 다국어 처리하기

01.다국어 처리의 개요

02.Locale 클래스를 이용한 다국어 처리

03.JSTL fmt 태그를 이용한 다국어 처리

 

----------------------------------------------------

 

 

PART 03 페이지 처리 기법

 

chapter 08 유효성 검사: 도서 등록 데이터의 유효성 검사하기

 

01.유효성 검사의 개요

-유효성 검사(validation)는 사용자가 폼 페이지에서 입력한 데이터 값이 서버로 전송되기 전에 특정 규칙에 맞게 입력되었는지 검증하는 것을 말함. 다시 말해 사용자가 실수로 유효하지 않은 데이터 값을 입력하면 부적합하다고 판단하여 다시 폼 페이지로 되돌려 사용자에게 오류가 있음을 알려줌. 이러한 유효성 검사의 예로는 폼 페이지에서 나이를 입력할 떄 숫자를 인식하는 검사, 회원 가입 시 아이디 중복 검사, 로그인 인증 시 아이디와 비밀번호 검사, IP 패킷 검사 등을 들 수 있음

 

◎유효성 검사가 필요한 이유와 검사 항목

-웹 애플리케이션에서 폼 데이터의 유효성 검사가 필요한 가장 큰 이유는 보안 공격, 잘못된 데이터, 사용자의 실수로 예상 가능한 오류 등을 방지할 수 있기 때문임. 유효성 검사 기능을 이용하면 다양한 사용자가 폼 데이터를 입력해도 폼 페이지의 동일한 입력 양식과 행태를 유지할 수 있음. 입력 양식이 있는 폼 페이지를 만들 때 반드시 점검해야 할 유효성 검사의 항목은 다음과 같음

유효성 검사 항목 HTML JS Java
(1)입력 데이터가 null인지 확인하는 유효성 검사
ex)<input required>
O O O
(2)날짜나 이메일을 입력할 때 형식이 맞는지 확인하는 유효성 검사
ex)type="date"(선택), type="email"
O O O
(3)입력 데이터의 제한 길이를 초과했는지 확인하는 유효성 검사
ex)maxlength
O O O
(4)로그인 인증 시 아이디와 비밀번호를 확인하는 유효성 검사(Java) X X O
(5)회원 가입 시 아이디 중복 여부를 확인하는 유효성 검사(Java) X X O

 

1)유효성 검사를 위한 핸들러 함수

-핸들러 함수는 폼 페이지에서 이벤트가 밸생했을 때(<submit>을 클릭한 경우, onclick="핸들러 함수")의 유효성 검사를 위해 매핑하는 메소드로, 자바스크립트를 이용하여 유효성 검사를 위한 코드를 작성함. 자바스크립트는 웹 브라우저에서 유효성 검사를 처리하므로 서버에서 처리하는 것보다 속도가 빠르고 서버에 과부하를 주지 않음

-핸들러 함수에서는 사용자가 폼 페이지에 입력한 데이터 값이 서버로 전송되기 전에 특정 규칙에 맞게 입력되었는지를 검사함. 입력된 데이터가 유효성 검사를 통과하면 서버로 전송하고, 그렇지 않으면 서버 전송을 취소하고 사용자에게 오류 메시지를 보여주는 역할을 함

<script type="text/javascript">
function 핸들러 함수(){
       var str=document.폼이름.입력항목이름.value;
   }
</script>
<form name="폼이름">
       ··· (생략) ···
       <input type="submit" onclick="핸들러 함수()">
</form>

*HTML 제일 밑에 JS 넣기(실행 권장)

 연결방법: 1)onclick 2)addEventListener("click", click)

 

-유효성 검사를 위해 핸들러 함수를 만드는 과정은 다음과 같음

(1)input 태그의 type 속성 값이 submit인 경우 onclick 속성을 이용하여 핸들러 함수를 설정함. 또는 form 태그의 onsubmit 속성 값에 설정함

(2)자바스크립트를 이용하여 <script> ··· </script> 내에 핸들러 함수를 작성함. <script> ··· </script> 구문은 JSP 페이지의 어디에 위치해도 괜찮음

(3)폼 페이지에서 입력된 데이터 값을 핸들러 함수로 가져오기 위해 form 태그의 name 속성 또는 forms 객체를 이용함. forms 객체를 이용하는 경우, forms 객체는 배열의 형태이기 때문에 length 속성으로 크기를 알 수 있고 배열 값인 index는 form 태그가 나타나는 순서로 0부터 시작함

 

◎form 태그의 id 속성을 이용하여 핸들러 함수로 폼 입력 데이터 값을 가져오는 다른 방법

-form 태그가 <form id="loginForm">이라면 다음과 같이 접근함

var form=document.getElementsTagName("form")[0];
var form=document.getElementById("loginForm");

cf) JS: 1)select  2).querySelect("#...") / getElement

     CSS: 1)select( 클래스: .class/아이디: #id/태그: p{ })

 

2)유효성 검사 처리 방법

-사용자가 폼 페이지에 입력한 데이터 값이 서버로 전송되기 전에 웹 브라우저에서 검증하는 방법으로는 기본 유효성 검사와 데이터 형식 유효성 검사가 있음

 

유효성 검사의 종류

유효성 검사 설명
기본 유효성 검사 폼 페이지에 입력된 데이터 값의 존재 유무를 검사함
데이터 형식 유효성 검사 폼 페이지에 입력된 데이터 값이 특정 패턴에 적합한지 여부를 검사하며 정규 표현식을 사용함

 

◎유효성 검사

처리위치 코드 내용 키워드  
HTML 텍스트
<input type="text" required> input박스에 내용을 입력하지 않으면(공백) submit 이벤트를 실행할 수 없음 공백  
<input type="text" maxlength="10"> 입력글자의 최대 갯수를 10글자로 제한 최대길이
value="9" 값 미리 넣어두기
<input type="text" minlength="5" maxlength="10"> 최소길이를 제한하고싶다면 최대길이도 같이 제한하여야함 최소길이+최대길이  
HTML 숫자
<input type="number" > 숫자만입력하도록 강제할 수 있음 숫자입력  
<input type="number" min="10" > 숫자입력시 최소값을 강제할 수 있음 최소값입력제한  
<input type="number" max="10" > 숫자입력시 최대값을 강제할 수 있음 최대값입력제한  
<input type="number" min="5" max="10" > 숫자입력시 특정 범위의 값을 강제할 수 있음 범위값입력제한  
HTML 양식 <input type="email" required > 이메일 양식을 지키도록 강제 email 제한  
HTML 시간
<input type="date" required > 년월일을 선택할수 있도록 강제    
<input type="time" required > 시분초를 선택할수 있도록 강제    
<input type="datetime-local" required > 년월일시분초를 선택할 수 있도록 강제    
<input type="week" required > 주단위로 선택하도록 강제    
<input type="month" required > 년월을 선택하도록 강제    
<input type="date" min="2025-01-01" max="2025-12-31" > 선택날짜를 제한할 수 있다    
<input type="time" min="09:00:00" max="12:00:00" > 시간을 제한할 수 있다    
<input type="datetime-local" min="2025-01-01T09:00" max="2025-11-11T11:00" > 선택날짜와시간을 제한할 수 있음    
<input type="month" min="2025-01" max="2026-01"> 입력년월을 제한할수 있음    
HTML 패턴
<input type="text" pattern="[가-힣]+"> 한글만입력가능    
<input type="text" pattern="[0-9]+"> 숫자만입력가능    
  88 *190      

 

 

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <form action="a.jsp">
    <p>공백<input type="text" required="required"></p>
    <!-- 공백여부-->
    <p>최대길이<input type="text" maxlength="10"></p>
    <p>최소길이<input type="text" minlength="5" maxlength="10"></p>
    <!-- minlength는 maxlength와 같이 써야함-->
    <!-- 디스플레이: 보이는 크기(size)  -->
    <p>숫자입력<input type="number"></p>
    <p>숫자최소범위<input type="number" min="10"></p>
    <p>숫자최대범위<input type="number" max="30"></p>
    <p>숫자범위제한<input type="number" min="10" max="30"></p>
    <p>이메일양식<input type="email" required></p>
    <p>시간<input type="date" required></p>
    <p>날짜<input type="time" required></p>
    <p>날짜와시간<input type="datetime-local" required></p>
    <p><input type="week" required></p>
    <p><input type="month" required></p>

    <p>날짜입력제한<input type="date" min="2025-01-01" max="2025-12-31"></p>
    <p>시간입력제한<input type="time" min="09:00:00" max="12:00:00"></p>
    <p>날짜와시간입력제한<input type="datetime-local" min="2025-01-01T09:00:00" max="2025-11-11T11:00:00"></p>
    <p><input type="month" min="2025-01" max="2026-01"></p>
    <!-- 패턴 -->
    <p>한글패턴<input type="text" pattern="[가-힣]+"></p>
    <p>숫자패턴<input type="text" pattern="[0-9]+"></p>
     <!-- 한글만 입력가능: 한자리 (-) 가에서 힣~까지 (+) 계속 더함-->
    <p><input type="submit" value="전송"></p>
  </form>

</body>
</html>

 

실행 결과

 

02.기본 유효성 검사

-기본 유효성 검사는 사용자가 폼 페이지의 입력 항목에 입력한 데이터 값이 있는지 없는지 확인하고 데이터 길이, 숫자 등 기본적인 것이 맞는지 검사함. 이와 같이 폼 페이지의 입력 데이터 길이를 확인하여 데이터의 유무를 검증하는 것은 기본 유효성 검사에 해당함

 

1)데이터 유무 확인하기

-데이터 값의 유무에 대한 검사에서는 예를 들어 회원 가입 페이지에서 사용자가 아이디와 비밀번호 등의 필수 입력 항목을 입력하지 않고 <전송>을 클릭하면 입력하지 않았다는 오류 메시지가 나타남. 이때 입력 데이터의 유무를 검사하는 형식은 다음과 같음

document.폼이름.입력양식이름.value==" "

 

예제 8-2.폼 페이지에 입력한 데이터(아이디와 비밀번호) 값의 유무 검사하기

JSPBook/src/main/webapp/ch08/validation02.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Validation</title>
</head>


<body>
<form name="loginForm" action="ch08_2" method="post">
<P> 아 이 디 : <input type="text" name="id">
<P> 비밀번호 : <input type="password" name="passwd">
<P> <input type="button" value="전송" onclick="checkLogin()">
</form>
</body>
<script type="text/javascript">
function checkLogin(){
var form=document.loginForm;
console.log(form);
console.log(form.id.value);
if(form.id.value==""){
alert("아이디를 입력해주세요.");
form.id.focus();
return false;
}else if(form.passwd.value==""){
alert("비밀번호를 입력해주세요");
form.passwd.focus();
return false;
}
if(form.id.value.length < 4 || form.id.value.length > 12){
alert("아이디는 4~12자 이내로 입력 가능합니다!");
form.id.select();
return;
}

if(form.passwd.value.length < 4){
alert("비밀번호는 4자 이상으로 입력해야 합니다!");
form.passwd.select();
return;
}

form.submit();
}
</script>
</html>

1)select 2)수정, 생성, 삭제, 조회(CRUD)

*CRUD: Create(생성), Read(읽기), Update(갱신), Delete(삭제)

cf)submit = false 취소임, button 쓰면 상관 없음

   rerurn/ return false 반환할 때 값을 가지고 가는지의 차이일 뿐 별다른 의미는 없음

 

ch08_2_controller.java

package controller.ch08;


import java.io.IOException;


import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;


import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;


@WebServlet("/ch08_2")
public class ch08_2_controller extends HttpServlet{


@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doget 입장");

RequestDispatcher ds = req.getRequestDispatcher("ch08/validation02.jsp");
ds.forward(req, resp);
}


@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post 입장");

request.setCharacterEncoding("utf-8");
String id=request.getParameter("id");
String passwd=request.getParameter("passwd");

System.out.println(id);
System.out.println(passwd);

}


}

 

실행 결과

관리자임
1234

 

3)숫자 여부 확인하기

-숫자 여부는 isNaN() 함수를 활용하여 검사함. isNaN은 isNotaNumber의 약자이며, isNaN() 함수의 인자 값이 숫자이면 false를 반환하고 숫자가 아니면 true를 반환함

isNaN(document.폼이름.입력양식이름.value)

*substr(0, 1): 첫 글자(0시작 1앞까지 0만 해당)

 

조건에 맞지 않는 키 입력을 방지하는 방법

-키 입력에 관한 이벤트가 발생할 수 있도록 폼 페이지의 입력 양식에 onkeypress 속성을 사용하여 핸들러 함수를 설정함. 핸들러 함수를 통해 들어온 키 값을 구별하기 위해 event 객체의 keyCode 속성을 사용하며, 이 속성은 아스키코드 값으로 키 값을 구별 함

*character: A(65), a(97)

 keyCode: 키보드 값, onkeypress: 한글자 입력할 때마다

-event.returnValue는 입력받은 키 값의 반환 여부를 불 값으로 설정함. 앞의 예와 같이 event.returnValue를 false로 지정하면 조건문에 기술하지 않은 키 값을 받았을 때 그 값이 입력되지 않게 함. 그 외의 키 값은 다음과 같음

구분 키 값 범위
숫자만 입력 event.keyCode >= 48 && event.keyCode <= 57
영어만 입력 (event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 97 && event.keyCode <= 122)
특수문자만 입력 (event.keyCode >= 33 && event.keyCode <= 47) || (event.keyCode >= 58 && event.keyCode <= 64)
|| (event.keyCode >= 91 && event.keyCode <= 96) || (event.keyCode >= 123 && event.keyCode <= 126)
한글만 입력 event.keyCode >= 12592 || event.keyCode <= 12687

  

03.데이터 형식 유효성 검사

-데이터 형식 유효성 검사는 사용자가 폼 페이지의 입력 항목에 입력한 데이터 값이 특정 형태에 적합한지 검사하기 위해 정규 표현식(regular expression)을 사용하는 방법으로 기본 유효성 검사보다 복잡함

 

1)정규 표현식 사용하기

-정규 표현식은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어임. 즉 문자열의 특정 형태를 찾아내기 위해 패턴으로 표현한 수식임. 이러한 정규 표현식은 주민등록번호, 전화번호, 이메일과 같이 데이터 형식의 패턴이 일정한 데이터를 검사하는 데 이용하며, 다음 두 가지 형식 중 하나를 사용할 수 있음. 첫 번째는 객체 초기화(object initializer)를 사용하는 방법으로, 입력된 표현식이 거의 바뀌지 않는 상수 형태일 때 주로 사용함. 두 번째는 RegExp 객체를 이용하는 방법으로, 정규 표현식이 자주 변경될 때 주로 사용함

var 변수 이름 = /정규 표현식/[Flag];

 

var 변수 이름 = new RegExp('정규 표현식', ['Flag']);

 

-여기서 Flag는 정규 표현식의 끝인 / 다음에 오는 문자열로 생략할 수 있으며, 주로 사용하는 것은 다음과 같이 세 종류임

 

Flag의 종류

Flag 설명
i Ignore Case: 문자열의 대문자와 소문자를 구별하지 않고 검출함
g Global: 문자열 내의 모든 패턴을 검출함
m Multi Line: 문자열에 줄 바꿈 행이 있는지 검출함

 

-Flag를 설정하지 않으면 문자열 내에 검색 대상이 많이 있어도 한 번만 검출함. 정규 표현식에 따라 문자열을 판단하는 메소드는 다음과 같음

 

◎정규 표현식의 메소드 종류

메소드 설명
test() 매개변수 값으로 전달되는 문자열이 정규 표현식에 부합한지 판단하여 true/false를 반환함
exec() 매개변수 값으로 전달되는 문자열에서 정규 표현식에 부합된 문자열을 추출하여 반환함

 

2)정규 표현식의 표현 방법

-정규 표현식에서 사용하는 기호를 메타 문자라고 함. 메타 문자는 정규 표현식 내부에서 특정한 의미를 가진 문자로, 공통적인 기본 메타 문자의 종류는 다음과 같음

 

기본 메타 문자의 종류

메타 문자 설명
^x 문자열이 x로 시작됨
x$ 문자열이 x로 종료됨
.x 임의의 한 문자를 표현함(문자열이 x로 끝남)
x+ x가 한 번 이상 반복됨
x? x가 존재하거나 존재하지 않음
x* x가 0번 이상 반복됨
x|y x 또는 y를 찾음(or 연산자를 의미)
(x) () 안의 내용을 캡처하고 그룹화함
(x)(y) 그룹화할 때 자동으로 앞에서부터 그룹 번호를 부여해서 캡처함. 그룹화된 결과 데이터는 배열 형식으로 들어감
(x)(?:y) 캡처하지 않은 그룹을 생성할 경우 ?:를 사용함. 결과 값 배열에 캡처하지 않은 그룹은 들어가지 않음
x{n} x를 n번 반복한 문자를 찾음
x{n,} x를 n번 이상 반복한 문자를 찾음
x{n,m} x를 n번 이상 m번 이하 반복한 문자를 찾음

 

-메타 문자 중 독특한 성질을 띤 문자 클래스인 []가 있음. 문자 클래스는 그 내부에 해당하는 문자열의 범위 중 한 문자만 선택한다는 것을 의미하며, 문자 클래스 내부에서는 메타 문자를 사용할 수 없거나 의미가 다르게 사용됨

 

문자 클래스의 종류

문자 클래스 설명
[xy] x 또는 y를 찾음
[^xy] x,y를 제외하고 문자 하나를 찾음(문자 클래스 내의 ^은 not을 의미)
[x-z] x부터 z 사이의 문자 중 하나를 찾음
\^ ^(특수문자)을 식에 문자 지체로 포함함
\b 문자와 공백 사이의 문자를 찾음
\B 공백을 제외한 문자와 문자 사이의 문자를 찾음
\d 숫자를 찾음
\D 숫자가 아닌 값을 찾음
\s 공백 문자를 찾음
\S 공백이 아닌 문자를 찾음
\t Tab 문자를 찾음
\v Vertical Tab 문자를 찾음
\w 알파벳+숫자+_을 찾음
\W 알파벳+숫자+_을 제외한 모든 문자를 찾음

 

◎자주 사용되는 패턴

-다음은 폼 페이지에 입력 양식의 데이터 형식으로 주로 사용되는 정규 표현식의 예임

데이터 형식 패턴
숫자만 ^[0-9]*$
영문자만 ^[a-zA-Z]*$
한글만 ^[가- ]*$
영문자와 숫자만 ^[a-zA-Z0-9]*$
이메일 ^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z]*\.[a-zA-Z]{2,3}$/i
휴대전화 ^01(?:0|1|[6-9]) - (?:\d{3}|\d{4}) - \d{4}$
전화번호 ^\d{2,3} - \d{3,4}\ - \d{4}
주민등록번호 \d{6} \- [1-4]\d{6}
IP 주소 ([0-9]{1,3}) \. ([0-9]{1,3}) \. ([0-9]{1,3}) \. ([0-9]{1,3})
URL ^(file|gopher|news|nntp|telnet|https?|ftps?|sftp):\/\/([a-z0-9-]+\.)+[a-z0-9]{2,4}.*$
날짜 \d{1,2}\/\d{1,2}\/\d{2,4}$

cf)더 다양한 정규 표현식은 http://regexlib.com에서 확인할 수 있음

 

04.북마켓 도서 등록 데이터의 유효성 검사하기

-유효성 검사 처리 방법을 이용하여 도서 등록 페이지에서 사용자에게 도서 등록 데이터를 입력받아 유효성을 검증함

 

예제 8-6.도서 등록 페이지의 유효성 검사하기

1)자바스크립트 관리 폴더 만들기

- /src/main/webapp/resources/ 폴더에 자바스크립트 파일을 관리하는 폴더를 만들고 이 폴더에 validation.js 파일을 생성함

 

2)유효성 검사를 위한 핸들러 함수 작성하기

BookMarket/src/main/webapp/resources/js/validation.js

function CheckAddBook(){


var bookId=document.getElementById("bookId");
var name=document.getElementById("name");
var unitPrice=document.getElementById("unitPrice");
var unitsInStock=document.getElementById("unitsInStock");
var description=document.getElementById("description");


//도서아이디 체크
if(!check(/^ISBN[0-9]{4,11}$/, bookId, "[도서 코드]\nISBN과 숫자를 조합하여 5~12자까지 입력하세요\n첫 글자는 반드시 ISBN로 시작하세요"))
return false;


//도서명 체크
if(name.value.length < 4 || name.value.length > 50){
alert("[도서명]\n최소 4자에서 최대 50자까지 입력하세요");
name.focus();
return false;
}


//도서 가격 체크
if(unitPrice.value.length==0 || isNaN(unitPrice.value)){
alert("[가격]\n숫자만 입력하세요");
unitPrice.focus();
return false;
}


if(unitPrice.value < 0){
alert("[가격]\n음수를 입력할 수 없습니다");
unitPrice.focus();
return false;
}


//재고 수 체크
if(isNaN(unitsInStock.value)){
alert("[재고 수]\n숫자만 입력하세요");
unitsInStock.focus();
return false;
}


//상세설명 체크
if(description.value.length < 100){
alert("[상세설명]\n최소 100자 이상 입력하세요");
description.focus();
return false;
}


function check(regExp, e, msg){
if(regExp.test(e.value)){
return true;
}
alert(msg);
e.focus();
return false;
}


document.newBook.submit()


}

 

3)도서 등록 페이지 수정하기

BookMarket/src/main/webapp/addBook.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://kit.fontawesome.com/fa848000d2.js" crossorigin="anonymous"></script>
<link rel ="stylesheet" href ="./resources/css/bootstrap.min.css" />
-------------------------- 추가 -------------------------------

<script type="text/javascript" src="./resources/js/validation.js"></script>
<title>도서 등록</title>
</head>
<body>
<div class="container py-4">
<%@ include file="menu.jsp"%>


<div class="p-5 mb-4 bg-body-tertiary rounded-3">
<div class="container-fluid py-5">
<h1 class="display-5 fw-bold">도서 등록</h1>
<p class="col-md-8 fs-4">Book Addition</p>
</div>
</div>


<div class="row align-items-md-stretch">
<form name="newBook" action="./processAddBook.jsp" class="form-horizontal" method="post" enctype="multipart/form-data">
<div class="mb-3 row">
<label class="col-sm-2">도서코드</label>
<div class="col-sm-3">
-------------------------- 추가 ------------------------------- (id=" ")

<input type="text" id="bookId" name="bookId" class="form-control">
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">도서명</label>
<div class="col-sm-3">
<input type="text" id="name" name="name" class="form-control">
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">가격</label>
<div class="col-sm-3">
<input type="text" id="unitPrice" name="unitPrice" class="form-control">
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">저자</label>
<div class="col-sm-3">
<input type="text" name="author" class="form-control">
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">출판사</label>
<div class="col-sm-3">
<input type="text" name="publisher" class="form-control">
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">출판일</label>
<div class="col-sm-3">
<input type="text" name="releaseDate" class="form-control">
</div>
</div>


<div class="mb-3 row">
<label class="col-sm-2">상세정보</label>
<div class="col-sm-5">
<textarea name="description" id="description" cols="50" rows="2"
class="form-control" placeholder="100자 이상 적어주세요"></textarea>
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">분류</label>
<div class="col-sm-3">
<input type="text" name="category" class="form-control">
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">재고수</label>
<div class="col-sm-3">
<input type="text" id="unitsInStock" name="unitsInStock" class="form-control">
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">상태</label>
<div class="col-sm-5">
<input type="radio" name="condition" value="New"> 신규도서 <input
type="radio" name="condition" value="Old"> 중고도서 <input
type="radio" name="condition" value="EBook"> E-Book
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-2">이미지</label>
<div class="col-sm-5">
<input type="file" name="BookImage" class="form-control">
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-offset-2 col-sm-10">
<input type="button" class="btn btn-primary" value="등록" onclick="CheckAddBook()">
</div>
</div>
</form>
</div>
<jsp:include page="footer.jsp" />
</div>
</body>
</html>

 

실행 결과

 

 

chapter 09  다국어 처리: 도서 등록 페이지의 다국어 처리하기

 

01.다국어 처리의 개요

-다국어 처리는 웹 브라우저를 사용하는 국가에 따라 다양한 언어 및 지역을 지원하는 서비스를 말함. 즉 다른 언어와 지역적 차이를 기술 변경 없이 소프트웨어에 바로 적용하는 것임. 예전에는 국제적인 환경에서 서비스될 웹 사이트를 구출할 때 국가별로 페이지를 만들어야 했음. 이렇게 국가별로 언어로 변형하여 웹 사이트를 제작하고 유지 관리하는 일은 시간이 오래 걸리고 어려움

-하지만 JSP 페이지에 JSTL의 fmt 태그를 이용하면 언어별로 페이지를 따로 만들 필요 없이 아주 간단하게 다국어를 지원할 수 있음. 다국어는 다양한 언어와 지역에 적용될 수 있도록 하는 국제화(internationalization, i18n)와 언어별 구성 요소를 추가하여 특정 지역의 언어나 문화에 맞추는 지역화(localization, L10n)를 포함함

 

i18n과 L10n의 의미

-국제화를 I18N 또는 i18n으로 표기하고 지역화를 L10N 또는 l10n으로 표기하기도 함. 이는 internationalization과 localizatio의 첫 글자와 마지막 글자를 제외한 나머지 글자를 그 수로 표현한 것임. 즉 I18N은 'I+18자+N 

'을 의미함. 그런데 L의 소문자와 대문자 I가 비슷해 보이기 때문에 i18n과 L10n을 주로 사용함. 그 외에 다국어화(miltilingualization, m17n)라는 표현을 사용하기도 함

 

1)지역화

-지역화(localization)는 사용 국가별 환경에서 특정 언어와 지역에 맞게 적합화하는 것으로, 줄여서 L10n으로 표기하기도 함. 지역화에 주로 고려되는 사항은 다음과 같음

·숫자, 날짜, 시간의 형식

·화폐의 표시

·키보드의 지원

·문자열의 순서와 정렬

·심벌, 아이콘, 색상

·문화에 따라 오해의 소지가 있거나 의미가 없는 문자, 그림

·지역별 법률의 차이

 

2)국제화

-국제화(internationalization)는 여러 국가에서 사용할 수 있도록 다국어를 지원하는 것으로, 줄여서 i18n으로 표기하기도 함. 국제화는 어느 국가에서나 사용할 수 있게 하는 지역화 기능을 포함함. 국제화는 주로 다음과 같은 처리를 포함하여 지원해야 함

·국제화 지원은 유니코드의 사용이나 기존의 인코딩을 적절히 처리하고 사용자 인터페이스에 표시할 문자열에는 문자 코드가 포함되지 않도록 설계 및 개발해야 함

·국제화를 처리하는 정보에 언어 정보를 포함하거나, 세로쓰기/가로쓰기/우측에서 좌측으로의 가로쓰기 등 언어의 특성을 반영하는 처리 등을 지원해야 함

·날짜와 시간 표시, 지역의 달력, 숫자 표시, 리스트의 정렬과 표시, 인명이나 주소의 처리 등 언어의 특성(언어적 ·지역적 ·문화적 특성 등)에 대한 사용자 설정을 지원해야 함

·국제화는 사용자의 요청이나 설정에 따라 필요시 사용되도록 지역화 정보를 코드와 분리해야 함

 

02.Locale 클래스를 이용한 다국어 처리

-Locale 클래스는 특정 지리적·정치적·문화적 지역을 나타내는 클래스로, 사용자의 지역 환경에 따라 결정되는 지역적 문화(언어, 날짜, 시간 등)의 정보를 담고 있음. 예를 들어 웹 페이지에 보이는 메시지가 여러 가지 언어로 주어졌을 때 사용자가 어떤 언어로 출력할 것인지 결졍할 수 있게 하는 순단이 바로 Locale 클래스임. Locale  클래스는 단순한 메시지뿐 아니라 숫자, 날짜, 시간 등을 표현하는 데 사용됨

-Locale 객체의 생성은 request 내장 객체를 이용하여 현재 웹 브라우저에 미리 정의된 언어나 국가 정보를 가져오는 방법으로 형식은 다음과 같음. Locale  클래스를 사용하려면 JSP 페이지에 page 디렉티브 태그의 import 속성으로 패키지 java.util.Locale을 설정해야 함

java.util.Locale request.getLocale();

 *싱글톤(하나)

 

◎Locale 클래스를 생성하는 또 다른 방법

·인스턴스화 방법

Locale locale=new Locale("ko", "KR");

*new 인스턴스 여러개

 

·미리 정의된 필드 값을 사용하는 방법

Locale locale=Locale.KOREA;

 

1)로케일 감지하기

-웹 브라우저에 설정된 국가와 언어 이름을 통해 사용자는 해당 국가의 언어로 웹 브라우저를 볼 수 있음. 이처럼 웹 브라우저에 설정된 국가와 언어 이름을 알아내는 것을 로케일 감지라고 함. 다음의 메소드를 이용하면 웹 브라우저에 설정된 국가나 언어 정보를 얻어올 수 있음

 

로케일 감지 메소드의 종류

메소드 반환 유형 설명
getDefault() static Locale 디폴트 로케일의 현재 값을 가져옴
getCountry() String 현재 로케일의 국가/지역 코드(대문자)를 가져옴
getDisplayCountry() String 현재 로케일의 국가 이름을 가져옴
getLanguage() String 현재 로케일의 언어 코드(소문자)를 가져옴
getDisplayLanguage() String 현재 로케일의 언어 이름을 얻어옴

 

2)로케일 표현하기

·언어 설정

-다양한 국가별 언어를 제대로 표현하기 위해 response 내장 객체의 setHeader() 메소드를 사용함. 이 메소드에 Content-Language 헤더 값을 언어 코드로 설정하고 HTML 엔티티 코드나 이름을 사용하면 모든 특수문자를 출력할 수 있음

·날짜와 시간 설정

-사용자의 로케일에 따라 특정 날짜와 시간 형식을 표현하기 위해 DateFormat 클래스의 getDateTimeInstance() 메소드를 사용함. DateFormat 클래스를 사용하려면 JSP 페이지에 page 디렉티브 태그의 import 속성으로 패키지 java.text.DateFormat을 설정해야 함

·통화와 숫자 설정

-사용자의 로케일에 따라 특정 통화와 숫자를 표현하기 위해 NumberFormat 클래스의 getCurrencyInstance() 메소드를 사용함. 또한 특정 비율을 표현하는 데에는 getPercentInstance() 메소드를 사용함. NumberFormat 클래스를 사용하려면 JSP 페이지에 page 디렉티브 태그의 import 속성으로 패키지 java.text.NumberFormat을 설정해야 함

 

예제 9-1.사용자의 로케일에 따라 국가, 날짜, 통화 형식 출력하기

package controller.ch09;


import java.io.IOException;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.Locale;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;


@WebServlet("/ch09_1")
public class ch09_1_controller extends HttpServlet{


@Override
protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doget 입장");

Locale locale=request.getLocale();
Date currentDate=new Date();
DateFormat dateFormat=DateFormat.getDateInstance(DateFormat.FULL, locale);
NumberFormat numberFormat=NumberFormat.getNumberInstance(locale);

System.out.println("국가 : " + locale.getDisplayCountry());
System.out.println("날짜 : " + dateFormat.format(currentDate));
System.out.println("숫자 (12345.67) : " + numberFormat.format(12345.67));
}


@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post 입장");


}

}

 

실행 결과

국가 : 대한민국
날짜 : 2025년 2월 14일 금요일
숫자 (12345.67) : 12,345.67

 

03.JSTL fmt 태그를 이용한 다국어 처리

-JSTL fmt 태그는 다국어 문서 처리를 위한 국제화 및 지역화 태그임. 날짜와 숫자 등을 형식화하는 기능을 제공하는 JSTL 라이브러리인 JSTL fmt 태그는 특정 지역에 따라 다른 메시지를 출력할 때 사용함. 예를 들면 한글 웹 브라우저는 한글 메시지를 출력할 때, 영문 웹 브라우저는 영어 메시지를 출력할 때 유용함

-이와 같이 JSTL fmt 태그는 중복 작업을 없애고 하나의 JSP 페이지에서 다양한 언어에 맞는 메시지를 출력하게 함. JSTL fmt 태그 라이브러리를 사용하려면 다음과 같이 JSP 페이지에 taglib 디렉티브 태그로 서식 라이브러리를 포함해야 함. 또한 JSTL 라이브러리인 jstl.jar 파일이 필요함

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

 

-여기서 uri 속성은 사용자가 정의한 태그의 설정 정보가 담긴 경로 주소임. prefix 속성은 uri에 설정한 주소를 사용자가 정의한 태그로 식별하는 데 쓰이는 고유 이름임. uri 속성 값을 그대로 사용하면 복잡하므로 prefix 속성 값이 대신 식별할 수 있게 해주는 것임

·배포 사이트: http://mvnrepository.com

·다운로드 파일: jstl-1.2_2.jar

 

◎JSTL fmt 태그의 종류

구분 태그 유형 설명
Locale 설정 setLocale 로케일을 설정함
requestEncoding 요청 파라미터의 문자 인코딩을 설정함
메시지 처리
ex)<link src=""> 연결코드
bundle 사용할 리소스번들을 설정함
message 리소스번들에서 로케일에 맞는 메시지를 가져와 출력함
setBundle 리소스번들을 읽어와 특정 변수에 저장함
날짜 formatDate 날짜 형식을 표현함
parseDate 문자열에서 원하는 패턴의 날짜 형식으로 변환함
숫자 parseNumber 문자열에서 원하는 패턴의 숫자 형식으로 변환함
formatNumber 숫자 형식을 표현함
시간 setTimeZone 특정 범위의 시간대를 설정함
timeZone 시간대를 설정함

 

아파치 톰캣에 제공하는 JSTL 라이브러리 설치 방법

(1)아파치 톰캣 사이트(http://tomcat.apache.org)에 접속하여 [Download]-[Taglibs] 메뉴를 선택한 후 taglibs-standard-impl-1.2.5.jar, taglibs-standard-spec-1.2.5.jar, taglibs-standard-jstlel-1.2.5.jar 파일을 모두 다운로드함

(2)다운로드한 taglibs-standard-impl-1.2.5.jar, taglibs-standard-spec-1.2.5.jar, taglibs-standard-jstlel-1.2.5.jar을 톰캣 10버전으로 마이그레이션함

(3)웹 애플리케이션의 /src/main/webapp/WEB-INF/lib/ 폴더에 마이그레이션한 모든 파일을 포함함. 또는 서버 톰캣의 lib/ 폴더와 자바 JDK의 lib 폴더에 마이그레이션한 모든 파일을 포함함

 

1)로케일 설정 태그의 기능과 사용법

·setLocale 태그

-setLocale 태그는 국제화 태그가 사용할 로케일을 설정하는 태그로 형식은 다음과 같음. setLocale 태그는 다국어를 지원하는 웹 페이지를 만들 때 리소스번들(ResourceBundle)인 *.properties 파일과 연계하여 사용함

<fmt:setLocale
    value="언어 코드[_국가 코드]"
   [scope="{page|request|session|application}"] /> //기본값은 page

*객체의 수명: 라이프사이클

 

-value 속성 값에서 언어 코드는 두 글자로 된 소문자로서 필수이고, 국가 코드는 두 글자로 된 대문자로서 추가로 설정함. 언어 코드와 국가 코드를 모두 설정하려면 붙임표(-)나 밑줄(_)로 구분해야 함. scope 속성 값으로 page, request, session, ·  application 중 하나를 설정하거나 생략할 수 있는데, 생략하면 기본값인 page로 설정됨

 

·requestEncoding 태그

requestEncoding 태그는 요청 파라미터의 문자 인코딩을 설정하는 태그로 형식은 다음과 같음. 이 태그는 request 내장 객체의 setCharacterEncoding() 메소드와 동일한 역할을 함

<fmt:requestEncoding value="문자 인코딩" />

*euc-kr 다른 프로그램에서 실행시 한글깨질 수 있음(UTF-8 사용 권고)

 

2)메시지 처리 태그의 기능과 사용법

·리소스번들

-리소스번들은 메시지 처리 태그에서 사용하는 파일로 메시지번들이라고도 함. 리소스번들로 사용하는 파일은 보통 WEB-INF/classes/ 폴더에 있음. 리소스번들은 java.util.properties 클래스에 정의된 방법으로 메시지를 읽어오기 때문에 확장자가 properties인 파일이 반드시 있어야 함. java.util.properties 클래스는 알파벳이나 숫자, 라틴 문자 외의 언어를 유니코드 값으로 표현함

 

리소스번들로 사용하는 *.properties 파일의 종류

*.properties 파일(확장자) 설명
파일 이름.properties 기본 메시지일 때 사용함(default)
파일 이름_ko.properties 한글 메시지일 때 사용함
파일 이름_en.properties 영어 메시지일 때 사용함

*setLocale value 매핑

 

[한글, 영문 리소스번들 작성 예]

//message_ko.properties 파일인 경우
NAME =홍길순

*NAME(key=value)

//message_en.properties 파일인 경우
NAME =your name

 

리소스번들로 사용하는 *.properties 파일

-리소스번들로 사용하는 *.properties 파일은 보통 WEB-INF/classes/ 폴더 안에 위치하며 디렉터리의 깊이에 따라서 패키지 형식의 이름으로 되어 있음. 로케일이 ko이면 testBundle_ko.properties 파일을 읽어오고, 로케일이 맞지 않으면 testBundle.properties처럼 언어 코드가 붙지 않은 파일을 읽어옴

-*.properties 파일의 내용은 한글로 직접 입력할 수 없기 때문에 한들에 해당하는 유니코드로 변환해서 입력해야 함. 또는 이클립스에서 PropertiesEditor 플러그린을 사용하면 좀 더 쉽게 작성 할 수 있음

* 다국화: message_ko.properties, message_en.properties의 출력 메시지 한글, 영어로 스위칭 가능

 

·bundle 태그

bundle 태그는 사용할 리소스번들을 설정하는 태그로 형식은 다음과 같음. 이 태그는 리소스번들로 사용할 *.properties 파일을 읽어오는 역할을 하며 message 태그와 함께 사용함

<fmt:bundle basename="리소스번들" [prefix="key 이름"] >
     //body 내용
</fmt:bundle>

 

-여기서 basename 속성 값의 리소스번들에는 반드시 확장자가 properties인 파일을 설정해야 함. prefix 속성 값은 bundle 태그 내부에서 사용되는 message 태그의 key 속성 값 앞에 자동으로 붙는 문자열을 의미함

 

·message 태그

-message 태그는 bundle 태그에 설정한 리소스번들에서 메시지를 읽어와 출력하는 태그로 형식은 다음과 같음

<fmt:message
      key="메시지 key 이름"
      [bundle="setBundle 태그의 변수 이름"]
      [var="메시지를 저장하는 변수 이름"]
      [scope="{page|request|session|application}"] /> //기본값은 page

 

-메시지 처리 태그를 이용하여 리소스번들의 메시지를 가져와 출력함. 여기서 key 속성 값은 리소스번들로 사용하는 *.properties 파일에서 메시지를 읽어와 var 속성 값인 메시지 변수에 저장함

 

·setBundle 태그

-setBundle 태그는 리소스번들을 가져와 변수로 저장한 후 JSP 페이지 어디에서나 사용할 수 있는 태그로 형식은 다음과 같음. 이 태그는 bundle 태그를 대체하여 사용할 수 있음

<fmt:setBundle
      basename="리소스번들"
      [var=" 리소스번들을 저장할 변수 이름"]
      [scope="{page|request|session|application}"] /> //기본값은 page

 

-여기서 var 속성 값에 설정한 변수는 message 태그의 bundle 속성 값에 사용함

 

예제 9-2.사용자의 로케일에 따라 리소스번들의 메시지 출력하기

JSPBook/src/main/java/ch09/myBundle_ko.properties

title=\uC790\uBC14 \uC11C\uBC84 \uD398\uC774\uC9C0(자바 서버 페이지)
username=\uAD00\uB9AC\uC790(관리자)
password=1234

*입력된 한글은 자동으로 유니코드로 변환

JSPBook/src/main/java/ch09/myBundle_en.properties

title=Java Server pages
username=admin
password=1234

 

3)/src/main/webapp/ch09/ 폴더에 다음과 같이 웹 페이지를 작성하고 실행 결과 확인함

JSPBook/src/main/webapp/ch09/jstl_fmt01.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Internationalization</title>
</head>
<body>
<p> ------기본 로케일------
<fmt:setLocale value="ko" />
<fmt:setBundle basename="controller.ch09.myBundle" var="resourceBundle" />
<p> 제목 : <fmt:message key="title" bundle="${resourceBundle}" />
<p><fmt:message key="username" var="userMsg" bundle="${resourceBundle}"/>
<p> 이름 : ${userMsg}

<p> ------영문 로케일------
<fmt:setLocale value="en" />
<fmt:setBundle basename="controller.ch09.myBundle" var="resourceBundle" />
<p> 제목 : <fmt:message key="title" bundle="${resourceBundle}" />
<p> 이름 : <fmt:message key="username" bundle="${resourceBundle}"/>
</body>
</html>

*var: 패키지를 변수 처리 함(key 값 담은 걸 출력)

 var(x): 그냥 출력

 

실행 결과