chapter5-1
01. 내장 객체의 개요
내장객체(implicit object)는 jsp 페이지에서 사용할수 있도록 jsp 컨테이너에 미리 정의된 객체로 그 종류가 다양하다. jsp 페이지가 서블릿 프로그램으로 번역될 때 jsp 컨테이너가 자동으로 내장 객체를 멤버 변수, 메소드 매개변수 등의 각종 참조 변수(객체)로 포함한다. 그래서 jsp 페이지에 별도의 import 문 없이 자유롭게 사용할 수 있다. 그리고 스크립틀릿 태그나 표현문 태그에 선언을 하거나 객체를 생성하지 않고도 직접 호출하여 사용할 수 있다.
내장객체의 종류
내장 객체 반환 유형 설명
request | javax.servlet.http.HttpServletRequest | 웹 브라우저의 HTTP 요청 정보를 저장한다. |
response | javax.servlet.http.HttpServletResponse | 웹 브라우저의 HTTP 요청에 대한 응답 정보를 저장한다. |
out | javax.servlet.jsp.jsp.jspWriter | jsp 페이지에 출력할 내용을 담고 있는 출력 스트림이다. |
session | javax.servlet.http.HttpSession | 웹 브라우저의 정보를 유지하기 위한 세션 정보를 저장한다 |
application | javax.servlet.ServletContext | 웹 애플리케이션의 콘텍스트 정보를 저장한다. |
pageContext | javax.servlet.jsp.PageContext | jsp페이지의 정보를 저장한다. |
page | java.lang.Object | jsp페이지를 구현한 자바 클래스로 jsp 페이지 자체를 나타낸다. |
config | javax.servlet.ServletConfig | jsp페이지의 설정 정보를 저장한다 |
exception | java.lang.Throwable | jsp페이지의 예외 발생을 처리한다. |
2장에서 작성한 scriping.jsp 파일을 살펴보면 jsp페이지가 자바 서블릿 프로그램으로 번역될때 내장 객체가 자동으로 포함된 것을 확인할 수 있다.
다음과 같이 내장객체는 서블릿 프로그램에서 모두 _jspService()메소드 내부에 있다.
그리고 메소드 매개변수인 request,reponse 를 비롯해 pageContext, session, application, config, out, page 등은 메소드 내에서 참조할 수 있는 참조 변수이다.
모든 내장 객체는 jsp 컨테이너가 관리하는 객체로 이중 request, session, application, pageContext 를 이용하여 속성을 관리 할 수 있다. 속성은 각각의 내장객체가 존재하는 동안 jsp 페이지 사이에서 정보를 주고받거나 공유하는데 사용된다. 이렇게 속성을 처리하는 메소드는 다음과 같이 네종류이다.
속성 처리 메소드의 종류
메소드 반환 유형 설명
setAttribute(String name,Objext value) | void | 해당 내장 객체의 속성 이름이 name 인 속성 값을 value로 저장한다. |
getAttribute(String name) | Object | 해당 내장 객체의 속성 이름이 name 인 속성 값을 가져온다. |
removeAttribute(String name) | void | 해당 내장 객체의 속성 이름이 name 인 속성을 삭제한다. |
getAttributeNames() | java.util.Enumeration | 해당 내장 객체의 모든 속성 이름을 가져온다. (단 pageContext 내장 객체는 이메소드를 제공하지 않는다) |
02. request 내장 객체의 기능과 사용법
request 내장객체는 jsp페이지에서 가장 많이 사용되는 기본 내장 객체로, 웹 브라우저에서 서버의 jsp 페이지로 전달하는 정보를 저장한다. 즉 폼 페이지로부터 입력된 데이터를 전달하는 요청 파라미터 값을 jsp 페이지로 가져온다. jsp 컨테이너는 웹 브라우저에서 서버로 전달되는 정보를 처리하기 위해 javax.servlet.http.HttpServletRequest 객체 타입의 request 내장 객체를 사용하여 사용자의 요구사항을 얻어낸다.
2.1 요청 파라이터 관련 메소드
요청 파라미터는 사용자가 폼 페이지에 데이터를 입력한 후 서버에 전송할때 전달되는 폼페이지의 이볅된 정보 형태를 말한다. 이러한 요청 파라이터는 <name=value> 형식으로 웹 브라우저에서 서버의 jsp 페이지로 전송된다.
요청 파라미터는 폼 페이지에서 <input type=”text”..>처럼 입력 양식이 텍스트 유형인 경우 값을 입력하지 않으면 서버로 빈 문자열이 전송된다. 하지만 체크박스와 라디오 버튼 유형인 경우 선택하지 않고 전송하면 요청 파라미터 자체가 전달되지 않는다.
요청 파라미터 관련 메소드의 종류
요청 파라미터 관련 메소드 반환 유형 설명
getParameter(String name) | String | 요청 파라미터 이름이 name 인 값을 전달받는다. 요청 파라미터 값이 없으면 null을 반환한다. |
getParameterValues(String name) | String[] | 모든 요청 파라미터 이름이 name 인 값을 배열 형태로 전달받는다. 요청 파라미터 값이 없으면 null을 반환한다. |
getParameterNames() | java.util.Enumeration | 모든 요청 파라미터의 이름과 값을 Enumeration 객체 타입으로 전달받는다. |
getParameterMap() | java.util.Map | 모든 요청 파라미터의 이름과 값을 Map 객체 타입으로 전달받는다[Map 객체 타입은(요청 파라미터 이름, 값)형식으로 구성된다.] |
다음은 request 내장 객체의 getParameter()메소드를 이용하여 폼 페이지에서 입력된 데이터를 전달하는 요청 파라미터 값을 출력하는 예이다. 폼의 입력 양식에 ‘허시아’를 입력하고 <전송>을 클릭하면 요청 파라미터가 ‘입력 양식 이름 = 값’ 형식인 ‘name=허시아’으로 전송되고, jsp 페이지는 전송된 요청 파라미터 이름 name 을 통해 값을 받는다. 만약 폼의 입력 양식 값을 입력하지 않으면 요청 파라미터의 값으로 빈 문자열이 전송된다.
[request 내장 객체 사용 예: 요청 파라미터 값 출력하기]
/request.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<form action="process.jsp" method="post">
<p>
이 름 : <input type="text" name="name">
<input type="submit" value="전송">
</form>
</body>
</html>
/process.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<%
request.setCharacterEncoding("utf-8");
String name = request.getParameter("name");
%>
<p> 이 름 : <%=name %>
</body>
</html>
[request 내장 객체로 폼 페이지로부터 아이디와 비밀번호를 전송받아 출력하기
/request01.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<form action="request01_process.jsp" method="post">
<p> 아 이 디 : <input type="text" name="id">
<p> 비밀번호 : <input type="text" name="passwd">
<p> <input type="submit" value="전송"/>
</form>
</body>
</html>
/request01_process.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<%
request.setCharacterEncoding("utf-8");
String userid = request.getParameter("id");
String password = request.getParameter("passwd");
%>
<p>아이디 : <%=userid %>
<p>비밀번호 : <%=password %>
</body>
</html>
2.2 요청 HTTP 헤더 관련 메소드
웹 브라우저는 HTTP 헤더에 부가적인 정보를 담아 서버로 전송한다. 아래 표와 같이 request 내장 객체는 헤더 정보나 쿠키 관련 정보를 얻을 수 있는 메소드를 제공한다.
다음은 request 내장 객체의 HTTP 관련 메소드를 이용하여 헤더 정보를 출력하는 예로, 요청 HTTP 헤더 정보 중에서 헤더 이름 host 와 accept-language 의 값을 출력한다.
요청 HTTP 헤더 관련 메소드의 종류
요청HTTP헤더 관련메소드 반환유형 설명
getHeader(String name) | String | 설정한 name 의 헤더값을 가져온다. |
getHeaders(String name) | Enumeration | 설정한 name의 헤더 목록 값을 가져온다 |
getHeaderNames() | Enumeration | 모든 헤더 이름을 가져온다. |
getIntHeader(String name) | int | 설정한 name 의 헤더값을 정수로 가져온다. |
getDateHeader(String name) | long | 설정한 name 의 헤더값을 시간 값으로 가져온다. |
getCookies() | javax.servlet.http.Cookie | 모든 쿠키값을 가져온다. |
[request 내장 객체 사용 예: 요청 HTTP 헤더 정보 값 출력하기]
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<%
String hostValue = request.getHeader("host");
String alValue = request.getHeader("accept-language");
out.println("호스트명:" + hostValue );
out.println("설정된 언어:" +alValue);
%>
</body>
</html>
출력결과:
호스트명:localhost:8080 설정된 언어:ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
[request 내장 객체로 모든 HTTP 헤더 정보 값 출력하기]
<%@ page contentType="text/html; charset=utf-8" %>
<%@ page import="java.util.Enumeration" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<%
Enumeration en = request.getHeaderNames(); //모든 헤더 이름을 가져오도록 request 내장 객체의 getHeaderNames()메소드를 작성하고 이를모두 저장하도록 Enumeration 객체 타입의 변수 en을 작성한다.
while(en.hasMoreElements()){ //Enumeration객체타입의 변수 en의 hasMoreElements()메소드를 통해 저장된 헤더 이름이 있을 떄까지 반복하도록 while 문을 작성한다.
String headerName = (String) en.nextElement(); //현재 헤더이름을 가져오도록 Enumeration 객체 타입의 변수 en의 nextElement()메소드를 작성한다.
String headerValue = request.getHeader(headerName); //설정된 헤더 이름의 값을 가져오도록 request 내장 객체의 getHeader()메소드를 작성한다.
%>
<%= headerName %> : <%= headerValue %> <br> <!-- 현재 헤더이름과 값을 출력하도록 표현문 태그를 작성한다. -->
<%
}
%>
</body>
</html>
출력결과:
host : localhost:8080
connection : keep-alive
sec-ch-ua : "Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"
sec-ch-ua-mobile : ?0
sec-ch-ua-platform : "Windows"
upgrade-insecure-requests : 1
user-agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
sec-fetch-site : none
sec-fetch-mode : navigate
sec-fetch-user : ?1
sec-fetch-dest : document
accept-encoding : gzip, deflate, br
accept-language : ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
cookie : JSESSIONID=365AEE6C4CF6BFD7A92C8FD0D54E6793
2.3 웹 브라우저/서버 관련 메소드
아래 표와 같이 request 내장 객체는 웹 브라우저의 요청 및 서버 관련 정보를 얻을 수 있는 메소드를 제공한다.
웹 브라우저/서버 관련 메소드 반환 유형 설명
getRemoteAddr() | String | 웹 브라우저의 IP주소를 가져온다. |
getContentLenght() | long | 웹 브라우저의 요청 파라미터 길이를 가져온다. |
getCharacterEncoding() | String | 웹 브라우저의 문자 인코딩을 가져온다. |
getContentType() | String | 웹 브라우저의 콘텐츠 유형을 가져온다 |
getProtocol() | String | 웹 브라우저의 요청 프로토콜을 가져온다 |
getMethod() | String | 웹 브라우저의 요청 프로토콜을 가져온다. |
getRequestURI() | String | 웹 브라우저가 요청한 URI 경로를 가져온다. |
getContextPath() | String | 현재 jsp 페이지의 웹 애플리케이션 콘텍스트 경로를 가져온다 |
getServerName() | String | 서버 이름을 가져온다 |
getServerPort() | Int | 실행중인 서버 포트 번호를 가져온다. |
getQueryString() | String | 웹 브라우저의 전체 요청 파라미터 문자열[물음표(?)다음 URL에 할당된 문자열을 가져온다.] |
다음은 request 내장 객체의 웹 브라우저와 서버 관련 메소드를 이용하여 웹 브라우저와 서버정보를 출력하는 예이다. 웹 브라우저 정보로는 요청 정보 길이, 전송 방식, 요청 URI 등을, 서버정보로는 서버 이름과 포트를 출력한다. 앞의 예제 파일 rlequest.jsp 와 process.jsp 를 변경하여 출력해보자.
[request 내장 객체 사용 예: 웹 브라우저/서버 정보 출력하기]
request.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<form action="process.jsp" method="post">
<p>
이 름 : <input type="text" name="name">
<input type="submit" value="전송">
</form>
</body>
</html>
/process.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<%
request.setCharacterEncoding("utf-8");
String name = request.getParameter("name");
%>
<p>
이 름 : <%=name %> <br>
요청 정보 길이 : <%=request.getContentLength() %><br>
클라이언트 전송 방식 : <%=request.getMethod() %><br>
요청 URI : <%=request.getRequestURI() %><br>
서버 이름 : <%=request.getServerName() %><br>
서버 포트 : <%=request.getServerPort() %><br>
</body>
</html>
출력결과 :
이 름 : 허시아
요청 정보 길이 : 32
클라이언트 전송 방식 : POST
요청 URI : /JSPBook/ch05/process.jsp
서버 이름 : localhost
서버 포트 : 8080
[request 내장 객체로 모든 웹 브라우저 및 서버 정보 값 출력하기]
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<p>
클라이언트 IP : <%=request.getRemoteAddr() %> <br>
요청 정보 길이 : <%=request.getContentLength() %><br>
요청 정보 인코딩 : <%=request.getCharacterEncoding() %><br>
요청 정보 콘텐츠 유형 : <%=request.getContentType() %><br>
요청 정보 프로토콜 : <%=request.getProtocol() %><br>
요청 정보 전송방식 : <%=request.getMethod() %><br>
요청 URI : <%=request.getRequestURI() %><br>
콘텍스트 경로 : <%=request.getContextPath() %><br>
서버 이름 : <%=request.getServerName() %><br>
서버 포트 : <%=request.getServerPort() %><br>
쿼리문 : <%=request.getQueryString() %><br>
</body>
</html>
출력결과:
클라이언트 IP : 0:0:0:0:0:0:0:1
요청 정보 길이 : -1
요청 정보 인코딩 : null
요청 정보 콘텐츠 유형 : null
요청 정보 프로토콜 : HTTP/1.1
요청 정보 전송방식 : GET
요청 URI : /JSPBook/ch05/request03.jsp
콘텍스트 경로 : /JSPBook
서버 이름 : localhost
서버 포트 : 8080
쿼리문 : null
03. response 내장객체의 기능과 사용법
response 내장 객체는 사용자의 요청을 처리한 결과를 서버에서 웹 브라우저로 전달하는 정보를 저장한다. 즉 서버는 응답헤더와 요청 처리 결과 데이터를 웹 브라우저로 보낸다. jsp컨테이너는 서버에서 웹 브라우저로 응답하는 정보를 처리하기 위해 javax.servlet.http.HttpServletResponse 객체 타입의 response 내장 객체를 사용하여 사용자의 요청에 응답한다.
3.1페이지 이동 관련 메소드
사용자가 새로운 페이지를 요청할 때와 같이 페이지를 강제로 이동하는 것을 리다이렉션(redirection)이라고 한다. 서버는 웹 브라우저에 다른 페이지로 강제 이동하도록 response 내장 객체의 리다이렉션 메소드를 제공한다. 페이지 이동시에는 문자 인코딩을 알맞게 설정 해야 한다.
페이지 이동 방법
- 포워드(forward)방식 : 현재 jsp 페이지에서 이동할 URL로 요청 정보를 그대로 전달하므로 사용자가 최초로 요청한 정보가 이동된 URL에서도 유효합니다. 그러나 이동된 URL 이 웹 브라우저의 주소 창에 나타나지 않고 처름 요청한 URL 이 나타나기 때문에 이동여부를 사용자가 알 수 없다.
<jsp:forward page="이동할 페이지"/>
- 리다이렉트(redirect)방식 : 처음 요청받은 현재 jsp 페이지로부터 이동할 URL 을 웹 브라우저로 반호나한다. 이떄 웹 브라우저에서는 새로운 요청을 생성하여 이동할 URL 에 다시 요청을 전송하므로 처음 보낸 요청 정보가 이동된 URL에서는 유효하지 않는다. 즉 클라이언트가 새로 페이지를 요청한 것과 같은 방식으로 페이지가 이동한다. 따라서 이동된 URL 이 웹 브라우저의 주소 창에 보이는 것이다.
response.sendRedirect("이동할 페이지")
페이지 이동 관련 메소드의 종류
페이지 이동 관련 메소드 반환 유형 설명
sendRedirect(String url) | void | 설정한 URL 페이지로 강제 이동한다. |
다음은 response 내장 객체의 sendRedirect()메소드를 이용하여 www.google.com 으로 이동하는 예이다. 웹 브라우저에 response.jsp를 입력하면 이동된 페이지의 URL 인 www.google.com으로 바뀌어 나타난다.
[response 내장 객체 사용 예: 페이지 이동하기]
<%
response.sendRedirect("<http://www.google.com>");
%>
[response 내장 객체로 페이지 이동하기]
로그인을 위한 폼 페이지에서 사용자의 아이디와 비밀번호를 입력받고 이를 검증하는 예제이다. 아이디와 비밀번호가 일치하면 성공페이지로 이동하고, 그렇지 않으면 로그인 페이지로 이동한다.
/response01.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<form action="response01_process.jsp" mehod="post">
<p> 아 이 디 : <input type="text" name="id">
<p> 비밀번호 : <input type="text" name="passwd">
<p> <input type="submit" value="전송">
</form>
</body>
</html>
/response01_process.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<%
request.setCharacterEncoding("utf-8");
String userid = request.getParameter("id");
String password = request.getParameter("passwd");
if(userid.equals("관리자") && password.equals("1234")){
response.sendRedirect("response01_success.jsp");
}else{
response.sendRedirect("response01_failde.jsp");
}
%>
</body>
</html>
/response01_success.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
로그인 성공!
</body>
</html>
/response01_failed.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
로그인 실패
<a href="./response01.jsp">로그인 가기</a>
</body>
</html>
3.2응답 HTTP 헤더 관련 메소드
응답 HTTP헤더 관련 메소드는 서버가 웹 브라우저에 응답하는 정보에 헤더를 추가하는 기능을 제공한다. 헤더 정보에는 주로 서버에 대한 정보가 저장되어 있다.
응답HTTP헤더 관련 메소드 종류
응답 HTTP 헤더 관련 메소드 반환 유형 설명
addCookie(Cookie cookie) | void | 쿠키를 추가한다. |
addDateHeader(String name, long date) | void | 설정한 헤더이름 name 에 날짜/시간을 추가한다 |
addHeader(String name, String value) | void | 설정한 헤더이름 name 에 value를 추가한다 |
addIntHeader(String name,int value) | void | 설정한 헤더이름 name 에 정수값 value를 추가한다 |
setDateHeader(String name,long date) | void | 설정한 헤더이름 name 에 날짜/시간을 설정한다. |
setHeader(String name, String value) | void | 설정한 헤더이름 name에 문자열 값 value를 설정한다 |
setIntHeader(String name, int value) | void | 설정한 헤더이름 name 에 정수값 value 를 설정한다. |
containsHeader(String name) | boolean | 설정한 헤더이름 name이 HTTP헤더에 포함되었는지 여부를 확인한다. 포함하고 있는 경우 true를 반환하고 그렇지 않은 경우 false를 반환한다 |
getHeader(String name) | String | 설정한 헤더이름 name값을 가져온다. |
다음은 response 내장 객체의 응답 HTTP 헤더 관련 메소드를 이용하여 헤더 정보를 추가 및 설정하여 출력하는 에이다. contentType 헤더 이름을 추가하고 Cache-control, date 헤더 이름에 대해 값을 설정 한 후 각 헤더 이름에 대한 값을 가져와 출력한다.
[response 내장 객체로 5초마다 jsp페이지 갱신하기]
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<p>이 페이지는 5초마다 새로고침 됩니다.
<%
response.setIntHeader("Refresh",5); //5초마다 페이지가 갱신되도록 response 내장
%> //객체의 setIntHeader()메소드를 작성한다.
<p><%=(new java.util.Date()) %> //5초마다 갱신된 시각을 출력하도록 표현문 태그를 작성
</body>
</html>
출력결과:
이 페이지는 5초마다 새로고침 됩니다.
Thu Aug 31 05:33:28 KST 2023
3.3 응답 콘텐츠 관련 메소드
response 내장 객체는 웹 브라우저로 응답하기 위해 MIME 유형, 문자 인코딩, 오류 메시지, 상태 코드 등을 설정하고 가져오는 응답 콘텐츠 관련 메소드를 제공한다. 다음은 response 내장 객체의 응답 콘텐츠 관련 메소드를 이용하여 웹 브라우저의 콘텐츠 정보를 설정하는 예이다. 문자 인코딩과 콘텐츠 유형에 대해 값을 설정하고 각각의 값을 가져와 출력한다.
응답 콘텐츠 관련 메소드의 종류
응답 콘텐츠 관련 메소드 반환 유형 설명
ssetContentType(String type) | void | 웹 브라우저에 응답할 MIME 유형을 설정한다. |
getContentType() | String | 웹 브라우저에 응답할 MIME 유형을 가져온다. |
setCharacterEncoding(String charset) | void | 웹 브라우저에 응답할 문자 인코딩을 설정한다 |
getCharacterEncoding() | String | 웹 브라우저에 응답할 문자 인코딩을 가져온다 |
sendError(int status_code,String message) | void | 웹 브라우저에 응답할 오류(코드 및 오류 메시지)를 설정한다. |
setStatus(int statuscode) | void | 웹 브라우저에 응답할 HTTP 코드를 설정한다. |
[response 내장 객체로 오류 응답 코드와 오류 메시지 보내기]
<%@ page contentType="text/html; charset=utf-8" %>
<html>
<head>
<title>Implicit Objects</title>
</head>
<body>
<%
response.sendError(404,"요청 페이지를 찾을 수 없습니다. ㅎ");
%>
</body>
</html>
출력결과:
HTTP 상태 404 – 찾을 수 없음
타입 상태 보고
메시지 요청 페이지를 찾을 수 없습니다. ㅎ
설명 Origin 서버가 대상 리소스를 위한 현재의 representation을 찾지 못했거나, 그것이 존재하는지를 밝히려 하지 않습니다.
Apache Tomcat/9.0.78
'JSP, Servlet, MySQL > JSP - webmarket' 카테고리의 다른 글
폼 태그 : 상품 등록 페이지 만들기 (3) | 2023.09.03 |
---|---|
내장 객체 : 상품 상세 정보 표시하기 2 (0) | 2023.09.03 |
액션 태그 : 상품 목록 표시하기 3 (0) | 2023.09.03 |
액션 태그 : 상품 목록 표시하기 2 (0) | 2023.09.03 |
액션 태그 : 상품 목록 표시하기 (0) | 2023.09.03 |