반응형
1. AJAX엔진 1개 vs 다수
1) [AJAX엔진 1개] books.jsp문서를 AJAX 기능으로 요청하여 "XML 형식의 문서"로 응답 받아 태그로 변경하여 클라이언트에게 전달
💚 books/book_xhr.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
//<%-- books.jsp문서를 AJAX 기능으로 요청하여 XML 형식의 문서로 응답 받아 태그로 변경하여
//클라이언트에게 전달하는 JSP 문서 --%>
//<%-- => XSL 문서를 이용하여 XML 형식의 데이타를 HTML 태그로 변환하여 응답 처리 --%>
//<%-- => books.xsl 문서를 AJAX 기능으로 요청하여 XSL 문서를 응답받아 사용 --%>
//<%-- (파싱할 때 필요한 XSL 문서) --%>
//<%-- AJAX 기능을 제공받기 위해 자바스크립트 모듈(xhr.js - 전역변수와 함수) 사용 --%>
//<%-- => 문제점 : 하나의 XMLHttpRequest 객체만을 사용하여 요청과 응답 처리 - 다수의 웹프로그램 동시 요청 불가능 --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AJAX</title>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/xhr.js"></script>
</head>
<body>
<h1>교재목록</h1>
<hr>
<div id="books"></div>
<script type="text/javascript">
var xmlDoc=null; //XML 문서에 저장된 값을 저장하기 위한 전역변수
var xslDoc=null; //XSL 문서에 저장된 값을 저장하기 위한 전역변수
//books.jsp 문서를 AJAX 기능으로 요청하여 XML 문서로 응답받은 결과를 변수에 저장하는 함수
function loadBooksXML() {
sendRequest("get", "books.jsp", null, function() {
if(xhr.readyState==4){
if(xhr.status==200){
xmlDoc=xhr.responseXML;
loadBooksXSL(); //loadBooksXML() 처리결과 먼저 받고 loadBooksXSL() 호출
} else{
alert("에러코드 = "+xhr.status);
}
}
});
}
//books.sxl 문서를 AJAX 기능으로 요청하여 XSL 문서로 응답받은 결과를 변수에 저장하는 함수
function loadBooksXSL() {
sendRequest("get", "books.xsl", null, function() {
if(xhr.readyState==4){
if(xhr.status==200){
xslDoc=xhr.responseXML; //XSL도 일종의 XML임 , xslDoc에는 XSLT가 담길것임
doXSLT(); //loadBooksXSL() 처리결과 받고 doXSLT() 호출
} else{
alert("에러코드 = "+xhr.status);
}
}
});
}
//XSLT를 사용하여 XML 문서를 제공받아 HTML 문서로 변환하여 페이지를 변경하는 함수
function doXSLT() {
//alert(xmlDoc+" : "+xslDoc);
//파싱
//XSLTProcessor 객체 생성 - XSLT를 이용하여 XML 문서를 변환하기 위한 기능을 제공하는 객체
var xsltProcessor = new XSLTProcessor();
//XSLTProcessor.importStylesheet(xslt) :
//=> XSLTProcessor 객체에 변환 관련 정보가 저장된 XSLDocument 객체(XSLT)를 저장하기 위한 메소드
xsltProcessor.importStylesheet(xslDoc);
//XSLTProcessor.transformToFragment(xml,document):
//=> XML 문서를 전달받아 XSLTProcessor 객체에 저장된 변환 관련 정보(XSLT)를 사용하여
//=> document 객체의 자식 Element 객체로 반환
var fragment=xsltProcessor.transformToFragment(xmlDoc,document);
//반환받은 Element 객체를 특정 태그의 마지막 자식 태그로 추가하여 출력 처리
document.getElementById("books").appendChild(fragment);
}
/*
//비동기식 방식의 요청 - 가장 먼저 처리되는 함수 : doXSLT()일 것
//loadBooksXML() & loadBooksXSL()의 처리결과를 받는 것보다
//doXSLT()함수가 처리결과가 빠를 것이므로 null : null이 저장되는 것임
loadBooksXML();
loadBooksXSL();
doXSLT(); //null : null
*/
loadBooksXML(); //[object XMLDocument] : [object XMLDocument]
</script>
</body>
</html>
💚 js/xhr.js
/* AJAX Module : 전역변수와 함수를 이용하여 AJAX 기능 제공 */
var xhr=null;
function getXMLHttpRequest() {
if(window.ActiveXObject) {//IE4 ~ IE6
try {
return new ActiveXObject("msxml2.XMLHTTP");//IE5 ~ IE6
} catch (e) {
try {
return new ActiveXObject("MicrosoftXMLHTTP");//IE4
} catch (e) {
return null;
}
}
} else if(window.XMLHttpRequest) {//IE7 이상 또는 기타 브라우저
return new XMLHttpRequest();
} else {
return null;
}
}
/* 요청과 응답 처리를 위한 함수 선언 */
// 이 함수만 호출하면 내부적으로 XMLHttpRequest 객체를 만들어 요청에 대한 처리까지 가능함
// 즉, 매개변수만 잘 전달하면 AJAX가 자동으로 구현될 것임
function sendRequest(method, url, param, callback) {
xhr=getXMLHttpRequest();
//요청방식에 대한 검증과 저장
var httpMethod=method?method:"get"; //전달값이 있으면 변수에 저장 없으면 "get"저장
if(httpMethod!="get" && httpMethod!="post") {
httpMethod="get";
}
var httpParam=(param==null || param=="")?null:param;
//요청 URL 주소 저장
var httpUrl=url;
//GET 방식으로 요청시 전달값이 존재할 경우 URL 주소에 QueryString 추가
if(httpMethod=="get" && httpParam!=null) {
httpUrl=httpUrl+"?"+httpParam;
}
//응답결과를 제공받아 처리하기 위한 함수 등록
xhr.onreadystatechange=callback; //준비상태가 변경될 때마다 콜백함수 호출됨
//웹어플리케이션 요청
xhr.open(httpMethod, httpUrl);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(httpMethod=="post"?httpParam:null);
}
💚 books/books.jsp : xml파일
<?xml version="1.0" encoding="utf-8"?>
<%@ page language="java" contentType="text/xml; charset=UTF-8"
pageEncoding="UTF-8"%>
<books>
<book>
<title>Java의 정석</title>
<author>남궁성</author>
</book>
<book>
<title>JSP 웹프로그래밍</title>
<author>오정임</author>
</book>
<book>
<title>스프링 입문</title>
<author>유이치</author>
</book>
</books>
💚 books/books.xsl : xml 파일은 html 형식으로 변환해서 줄게요~
<?xml version="1.0" encoding="UTF-8"?>
<!-- XSL 문서 : XSLT 정보를 저장한 XML 문서 -->
<!-- XSLT(eXtensible Stylesheet Language Template) : XML 기반의 언어로 작성된 파서(Parser) -->
<!-- => XML 문서를 전달받아 HTML 또는 XML 문서로 변환하는 정보 제공 -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- output : XML 문서를 전달받아 변환하여 제공되는 문서의 형식을 설정하는 엘리먼트 -->
<!-- method 속성 : 변환 결과로 생성되는 문서의 형식을 속성값으로 설정 - 기본 : xml -->
<!-- encoding 속성 : 문서에서 사용하는 문자형태(캐릭터셋)을 속성값으로 설정 - 기본 : iso-8859-1 -->
<xsl:output method="html" encoding="utf-8"/>
<!-- template : XML 문서를 변환하기 위한 정보를 제공하는 엘리먼트 -->
<!-- match 속성 : XML 문서의 루트 엘리먼트를 속성값으로 설정 -->
<xsl:template match="books">
<!-- 처리 결과로 생성되는 문서에 대한 스타일 작성 -->
<ol>
<!-- for-each : XML 문서의 엘리먼트(태그)를 반복처리 하기 위한 엘리먼트 -->
<!-- select 속성 : 반복처리하기 위한 엘리먼트의 이름(태그명)을 속성값으로 설정 -->
<xsl:for-each select="book">
<!-- value-of : XML 문서의 엘리먼트 내용(값)을 반환받기 위한 엘리먼트 -->
<!-- select 속성 : 반환받기 위한 값의 엘리먼트의 이름(태그명)을 속성값으로 설정 -->
<li><b><xsl:value-of select="title"/></b>[<xsl:value-of select="author"/>]</li>
</xsl:for-each>
</ol>
</xsl:template>
</xsl:stylesheet>
2) [AJAX엔진 여러개] books.jsp문서를 AJAX 기능으로 요청하여 "XML 형식의 문서"로 응답 받아 태그로 변경하여 클라이언트에게 전달
💚books/books_ajax.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
//<%-- books.jsp문서를 AJAX 기능으로 요청하여 XML 형식의 문서로 응답 받아 태그로 변경하여
//클라이언트에게 전달하는 JSP 문서 --%>
//<%-- => XSL 문서를 이용하여 XML 형식의 데이타를 HTML 태그로 변환하여 응답 처리 --%>
//<%-- => books.xsl 문서를 AJAX 기능으로 요청하여 XSL 문서를 응답받아 사용 --%>
//<%-- (파싱할 때 필요한 XSL 문서) --%>
//<%-- AJAX 기능을 제공받기 위해 자바스크립트 모듈(ajax.js - 자바스크립트 객체) 사용 --%>
//<%-- => 해결법 : 다수의 XMLHttpRequest 객체를 사용하여 요청과 응답 처리 - 다수의 웹프로그램 동시 요청 가능 --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AJAX</title>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/ajax.js"></script>
</head>
<body>
<h1>교재목록</h1>
<hr>
<div id="books"></div>
<script type="text/javascript">
var xmlDoc=null; //XML 문서에 저장된 값을 저장하기 위한 전역변수
var xslDoc=null; //XSL 문서에 저장된 값을 저장하기 위한 전역변수
//books.jsp 문서를 AJAX 기능으로 요청하여 XML 문서로 응답받은 결과를 변수에 저장하는 함수
function loadBooksXML() {
//응답 결과를 제공받아 처리하는 콜백함수에는 XMLHttpRequest 객체를 저장하기 위한
//매개변수를 반드시 선언
new xyz.itwill.Ajax("get", "books.jsp", null, function(xhr) {
if(xhr.readyState==4){
if(xhr.status==200){
xmlDoc=xhr.responseXML; //books.jsp을 요청하는 [AJAX엔진1]
doXSLT();//loadBooksXML() 혹은 loadBooksXSL() 중 먼저 결과받은 사람이 doXSLT() 호출
} else{
alert("에러코드 = "+xhr.status);
}
}
});
}
//books.sxl 문서를 AJAX 기능으로 요청하여 XSL 문서로 응답받은 결과를 변수에 저장하는 함수
function loadBooksXSL() {
new xyz.itwill.Ajax("get", "books.xsl", null, function(xhr) {
if(xhr.readyState==4){
if(xhr.status==200){
xslDoc=xhr.responseXML;//books.xsl을 요청하는 [AJAX엔진2]
doXSLT();//loadBooksXML() 혹은 loadBooksXSL() 중 먼저 결과받은 사람이 doXSLT() 호출
} else{
alert("에러코드 = "+xhr.status);
}
}
});
}
//XSLT를 사용하여 XML 문서를 제공받아 HTML 문서로 변환하여 페이지를 변경하는 함수
function doXSLT() {
if(xmlDoc==null || xslDoc==null) return;
//alert(xmlDoc+" : "+xslDoc);
//파싱
//XSLTProcessor 객체 생성 - XSLT를 이용하여 XML 문서를 변환하기 위한 기능을 제공하는 객체
var xsltProcessor = new XSLTProcessor();
//XSLTProcessor.importStylesheet(xslt) :
//=> XSLTProcessor 객체에 변환 관련 정보가 저장된 XSLDocument 객체(XSLT)를 저장하기 위한 메소드
xsltProcessor.importStylesheet(xslDoc);
//XSLTProcessor.transformToFragment(xml,document):
//=> XML 문서를 전달받아 XSLTProcessor 객체에 저장된 변환 관련 정보(XSLT)를 사용하여
//=> document 객체의 자식 Element 객체로 반환
var fragment=xsltProcessor.transformToFragment(xmlDoc,document);
//반환받은 Element 객체를 특정 태그의 마지막 자식 태그로 추가하여 출력 처리
document.getElementById("books").appendChild(fragment);
}
loadBooksXML();
loadBooksXSL();
</script>
</body>
</html>
💚 js/ajax.js : 강사님이 임의대로 만든 모듈
/* AJAX Module : 객체(속성과 메소드)를 이용하여 AJAX 기능 제공 */
/* => 효율적인 비동기 요청에 대한 응답처리 제공 */
/* AJAX 여러번 사용하고 싶다면 XMLHttpRequest 객체가 여러개 생성될 수 있도록 */
var xyz={};
xyz.itwill={};
//생성자 함수
xyz.itwill.Ajax=function(method, url, param, callback) {
//객체 속성 추가
this.method=method;
this.url=url;
this.param=param;
this.callback=callback;
this.send();//메소드 호출
}
xyz.itwill.Ajax.prototype={
//XMLHttpRequest 객체를 생성하여 반환하는 메소드
getXMLHttpRequest:function() {
if(window.ActiveXObject) {//IE4 ~ IE6
try {
return new ActiveXObject("msxml2.XMLHTTP");//IE5 ~ IE6
} catch (e) {
try {
return new ActiveXObject("MicrosoftXMLHTTP");//IE4
} catch (e) {
return null;
}
}
} else if(window.XMLHttpRequest) {//IE7 이상 또는 기타 브라우저
return new XMLHttpRequest();
} else {
return null;
}
},
//XMLHttpRequest 객체를 이용하여 AJAX 요청하기 위한 메소드
send:function() {
//XMLHttpRequest 객체를 반환받아 객체 속성 추가
this.xhr=this.getXMLHttpRequest();
//요청방식에 대한 검증과 저장
var httpMethod=this.method?this.method:"get";
if(httpMethod!="get" && httpMethod!="post") {
httpMethod="get";
}
//전달값에 대한 검증과 저장
var httpParam=(this.param==null || this.param=="")?null:this.param;
//요청 URL 주소 저장
var httpUrl=this.url;
//GET 방식으로 요청시 전달값이 존재할 경우 URL 주소에 QueryString 추가
if(httpMethod=="get" && httpParam!=null) {
httpUrl=httpUrl+"?"+httpParam;
}
//응답결과를 제공받아 처리하기 위한 함수 등록
// => 응답결과 처리함수에 XMLHttpRequest 객체 제공
// => 객체 내부의 XMLHttpRequest 객체에 접근하지 못하도록 복사본 제공
var request=this;
this.xhr.onreadystatechange=function() {
//call 함수를 메소드의 this 변수에 전달될 객체 변경
request.onStateChange.call(request);
}
//웹어플리케이션 요청
this.xhr.open(httpMethod, httpUrl);
this.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
this.xhr.send(httpMethod=="post"?httpParam:null);
},
//응답결과를 처리하는 함수를 호출하는 메소드
onStateChange:function() {
this.callback(this.xhr);
}
};
💚books/books.jsp : xml파일
<?xml version="1.0" encoding="utf-8"?>
<%@ page language="java" contentType="text/xml; charset=UTF-8"
pageEncoding="UTF-8"%>
<books>
<book>
<title>Java의 정석</title>
<author>남궁성</author>
</book>
<book>
<title>JSP 웹프로그래밍</title>
<author>오정임</author>
</book>
<book>
<title>스프링 입문</title>
<author>유이치</author>
</book>
</books>
💚books/books.xsl : xml 파일은 html 형식으로 변환해서 줄게요~
2. XML vs JSON vs XML&JSON
1) member_xml_two.jsp 문서를 AJAX 기능으로 요청하여 회원목록을 "XML 형식의 문서"로 응답 받아 태그로 변경하여 클라이언트에게 전달
💛member_xml_one.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
//<%-- member_xml_two.jsp 문서를 AJAX 기능으로 요청하여 회원목록을 XML 형식의 문서로 응답 받아
//태그로 변경하여 클라이언트에게 전달하는 JSP 문서 --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AJAX</title>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/ajax.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/log.js"></script>
</head>
<body>
<h1>회원목록</h1>
<hr>
<div id="log"></div>
<script type="text/javascript">
new xyz.itwill.Ajax("get", "member_xml_two.jsp", null, function(xhr) {
if(xhr.readyState==4){
if(xhr.status==200){
var xmlDoc=xhr.responseXML;
//code 엘리먼트의 값을 반환받아 저장
var code = xmlDoc.getElementsByTagName("code").item(0).firstChild.nodeValue;
//log("code = " +code);
if(code=="success"){
var memberList = xmlDoc.getElementsByTagName("member");
for(let i=0; i<memberList.length; i++){
var member=memberList.item(i)
var id = member.getElementsByTagName("id").item(0).firstChild.nodeValue;
var name = member.getElementsByTagName("name").item(0).firstChild.nodeValue;
log("아이디 = "+id);
log("이름 = "+name);
}
//var member = xmlDoc.getElementsByTagName("member");
//var memberId = xmlDoc.getElementsByTagName("id");
//var memberName = xmlDoc.getElementsByTagName("name");
//for(let i=0; i<member.length; i++){
// var id = memberId.item(i).firstChild.nodeValue;
// var name = memberName.item(i).firstChild.nodeValue;
// log("아이디 = "+id);
// log("이름 = "+name);
// }
}else{
log("검색된 회원정보가 없습니다.");
}
}else{
log("에러코드 = "+xhr.status);
}
}
});
</script>
</body>
</html>
💛js/log.js
function log(message) {
var logElement=document.getElementById("log");
if(logElement!=null) {
logElement.innerHTML+=message+"<br>";
}
}
💛member_xml_two.jsp (XML 문서)
- 단점) 개발자가 코드 가져다 사용하기 힘듦
<?xml version="1.0" encoding="utf-8"?>
<%@ page language="java" contentType="text/xml; charset=UTF-8"
pageEncoding="UTF-8"%>
<result>
<code>success</code>
<data>
<member>
<id>abc123</id>
<name>홍길동</name>
</member>
<member>
<id>xyz789</id>
<name>임꺽정</name>
</member>
<member>
<id>tgb567</id>
<name>전우치</name>
</member>
<member>
<id>qwe963</id>
<name>일지매</name>
</member>
<member>
<id>mnb174</id>
<name>장길산</name>
</member>
</data>
</result>
2) member_json_two.jsp 문서를 AJAX 기능으로 요청하여 회원목록을 "JSON 형식의 문서"로 응답 받아 태그로 변경하여 클라이언트에게 전달
💛member_json_one.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
//<%-- member_json_two.jsp 문서를 AJAX 기능으로 요청하여 회원목록을 JSON 형식의 문서로 응답 받아
//태그로 변경하여 클라이언트에게 전달하는 JSP 문서 --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AJAX</title>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/ajax.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/log.js"></script>
</head>
<body>
<h1>회원목록</h1>
<hr />
<div id="log"></div>
<script type="text/javascript">
new xyz.itwill.Ajax("get", "member_json_two.jsp", null, function(xhr) {
if(xhr.readyState==4){
if(xhr.status==200){
var result = JSON.parse(xhr.responseText);
var code = result.code;
//log("code = "+code);
if(code=="success"){
var memberList=result.data;
for(i=0; i<memberList.length; i++){
var id = memberList[i].id;
var name = memberList[i].name;
log("아이디 = "+id);
log("이름 = "+name);
}
}else{
log("검색된 회원정보가 없습니다.");
}
}else{
log("에러코드 = "+xhr.status);
}
}
});
</script>
</body>
</html>
💛member_json_two.jsp (JSON 문서)
- 단점) 대량의 데이타를 전달하기에는 부적절
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
{
"code":"success",
"data":[
{"id":"abc123","name":"홍길동"},
{"id":"xyz789","name":"임꺽정"},
{"id":"tgb567","name":"전우치"},
{"id":"qwe963","name":"일지매"},
{"id":"mnb174","name":"장길산"}
]
}
3) member_xmljson_two.jsp 문서를 AJAX 기능으로 요청하여 회원목록을 "XML과 JSON 형식의 문서"로 응답 받아 태그로 변경하여 클라이언트에게 전달
💛member_jsonxml_one.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
//<%-- member_xmljson_two.jsp 문서를 AJAX 기능으로 요청하여 회원목록을 XML과 JSON 형식의 문서로 응답 받아
//태그로 변경하여 클라이언트에게 전달하는 JSP 문서 --%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AJAX</title>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/ajax.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/log.js"></script>
</head>
<body>
<h1>회원목록</h1>
<hr />
<div id="log"></div>
<script type="text/javascript">
new xyz.itwill.Ajax("get", "member_xmljson_two.jsp", null, function(xhr) {
if(xhr.readyState==4){
if(xhr.status==200){
var xmlDoc=xhr.responseXML;
var code=xmlDoc.getElementsByTagName("code").item(0).firstChild.nodeValue;
if(code=="success"){
var data=xmlDoc.getElementsByTagName("data").item(0).firstChild.nodeValue;
var memeberList=JSON.parse(data); //JSON 형식으로 표현된 data를 자바스크립트 객체로 변환
for(i=0; i<memeberList.length; i++){
var id=memeberList[i].id;
var name=memeberList[i].name;
log("아이디 = "+id);
log("이름 = "+name);
}
}else{
log("검색된 회원정보가 없습니다.")
}
}else{
log("에러코드 = "+xhr.status);
}
}
});
</script>
</body>
</html>
💛member_xmljson_two.jsp (XML + JSON 문서)
- 각각의 단점을 해결하기 위한 조합
- 전달은 XML, 실제 데이타 처리는 JSON
- 많이 사용하지는 않지만, 알아두면 좋음
<?xml version="1.0" encoding="utf-8"?>
<%@ page language="java" contentType="text/xml; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- XML + JSON 형식 : JSON 값은 CDATA형식으로 표현하여 값으로 처리할 수 있도록 만듦 -->
<!-- <![CDATA[]]> : 반드시(예<data>)바로 옆에다 코드 작성하기, 엔터 치고 코드 작성 하면 안됨 -->
<result>
<code>success</code>
<data><![CDATA[
[
{"id":"abc123","name":"홍길동"},
{"id":"xyz789","name":"임꺽정"},
{"id":"tgb567","name":"전우치"},
{"id":"qwe963","name":"일지매"},
{"id":"mnb174","name":"장길산"}
]
]]></data>
</result>
반응형
'web > javascript' 카테고리의 다른 글
[js - ajax] 16. ajax통신 예시 (0) | 2024.05.21 |
---|---|
[js - ajax] 14. Asynchronus + Javascript (0) | 2024.05.20 |
[js - jquery] 13. jqeury 이벤트 기능 | 에니메이션 기능 (0) | 2024.05.20 |
[js - jquery] 12. jquery 개념 | 선택자 | each() | css() | attr() | text() | html() | 태그 추가,삭제,이동 (0) | 2024.05.19 |
[js] 11. 자바스크립트 쿠키(Cookie) 사용법 (0) | 2024.05.19 |