커피판매관리 프로그램
MVC1 방식으로 제작하는 CRUD 프로그램
- 판매등록, 판매현황, 매장별판매액, 상품별판매액 확인 및 등록이 가능한 프로그램 만들기이다.
전체 디렉토리 구조는 아래캡쳐와 같다.
1. DB연결
프로젝트를 생성하기 전에 인코딩 설정을 UTF-8로 변경후, 톰캣과 오라클을 연결한다. 설치된 Oracle의 계정과 비밀번호를 확인한 후 작성한다.
1) DBOpen.java
package DBPKG;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBOpen {
public static Connection getConnection() throws Exception {
Class.forName("oracle.jdbc.OracleDriver");
Connection con = DriverManager.getConnection
("jdbc:oracle:thin:@//localhost:1521/xe","오라클아이디","오라클비밀번호");
return con;
}
}
2. 테이블 생성 및 데이터 삽입
요구조건대로 세 개의 테이블을 만들고 샘플데이터를 삽입하였다.
-- 1) 상품테이블
create table tbl_product_01(
pcode varchar2(10) NOT NULL Primary KEY
,name varchar2(20)
,cost number(10)
);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA01','아메리카노',3000);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA02','에스프레소',3500);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA03','카페라떼',4000);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA04','카라멜마끼',4500);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA05','카푸치노',5000);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA06','초코롤케익',6000);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA07','녹차롤케익',6500);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA08','망고쥬스',7000);
insert into tbl_product_01 ( pcode, name, cost ) values ('AA09','핫초코',2500);
-- 2) 매장테이블
create table tbl_shop_01(
scode varchar2(10) NOT NULL Primary KEY
,sname varchar2(20)
);
insert into tbl_shop_01 (scode, sname) values ('S001', '강남점');
insert into tbl_shop_01 (scode, sname) values ('S002', '강서점');
insert into tbl_shop_01 (scode, sname) values ('S003', '강동점');
insert into tbl_shop_01 (scode, sname) values ('S004', '강북점');
insert into tbl_shop_01 (scode, sname) values ('S005', '동대문점');
insert into tbl_shop_01 (scode, sname) values ('S006', '인천점');
-- 3) 판매테이블
create table tbl_salelist_01(
saleno number(10) NOT NULL Primary KEY
,pcode varchar2(10) NOT NULL
,saledate date
,scode varchar2(10) NOT NULL
,amount number(10)
);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100001, 'AA01', '20180902', 'S001', 50);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100002, 'AA03', '20180902', 'S002', 40);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100003, 'AA04', '20180902', 'S002', 20);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100004, 'AA04', '20180902', 'S001', 30);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100005, 'AA05', '20180902', 'S004', 40);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100006, 'AA03', '20180902', 'S004', 30);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100007, 'AA01', '20180902', 'S003', 40);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100008, 'AA04', '20180902', 'S004', 10);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100009, 'AA01', '20180902', 'S003', 20);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100010, 'AA05', '20180902', 'S003', 30);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100011, 'AA01', '20180902', 'S001', 40);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100012, 'AA03', '20180902', 'S002', 50);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100013, 'AA04', '20180902', 'S002', 50);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100014, 'AA05', '20180902', 'S004', 20);
insert into tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) values (100015, 'AA01', '20180902', 'S003', 30);
3. JSP 뷰페이지 만들기
1) 기본 nav 링크메뉴
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<a href="saleForm.jsp">판매등록</a>
<a href="saleList.jsp">판매현황</a>
<a href="shopList.jsp">매장별판매액</a>
<a href="proList.jsp">상품별판매액</a>
<a href="index.jsp">홈으로</a>
2) index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>매장별 커피 판매관리</title>
</head>
<body>
<header>
<h1>매장별 커피 판매관리 프로그램</h1>
<div class="nav">
<jsp:include page="mainMenu.jsp"></jsp:include>
</div>
</header>
<section>
<h2>매장별 커피 판매관리 프로그램</h2>
<p>매장별 커피 판매를 관리하기 위한 프로그램이다.</p>
<ol>
<li>상품테이블, 매장테이블, 판매테이블을 추가한다.</li>
<li>판매등록, 판매현황, 매장별판매액, 상품별판매액 페이지를 작성한다.</li>
<li>올바르게 구현되었는지 확인한다.</li>
</ol>
</section>
<footer>
<address>Copyright @2018 All right reserved Coffee House</address>
</footer>
</body>
</html>
3) saleList.jsp
<%@page import="net.coffee.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="java.util.*"%>
<%@ page import="java.util.ArrayList"%>
<%@ page import="net.utility.*" %>
<jsp:useBean id="dao" class="net.coffee.CoffeeDAO"></jsp:useBean>
<jsp:useBean id="dto" class="net.coffee.SaleDTO"></jsp:useBean>
<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>매장별 커피 판매관리</title>
</head>
<body>
<header>
<h1>매장별 커피 판매관리 프로그램</h1>
<div class="nav">
<jsp:include page="mainMenu.jsp"></jsp:include>
</div>
</header>
<section>
<h2>판매현황</h2>
<table border='1'>
<tr>
<th>비번호</th>
<th>상품코드</th>
<th>판매날짜</th>
<th>매장코드</th>
<th>상품명</th>
<th>판매수량</th>
<th>총판매액</th>
</tr>
<%
ArrayList<SaleDTO> list = dao.salelist();
if (list == null) {
out.println("<tr>");
out.println(" <td colspan='4'>결과를 찾을 수 없습니다.</td>");
out.println("</tr>");
} else {
for (int i = 0; i < list.size(); i++) {
dto = list.get(i);
%>
<tr>
<td><%=dto.getSaleno()%></td>
<td><%=dto.getPcode()%></td>
<td><%=dto.getSaledate().substring(0, 10)%></td>
<td><%=dto.getScode()%></td>
<td><%=dto.getName()%></td>
<td><%=dto.getAmount()%></td>
<td><%=Utility.comma(dto.getTot())%></td>
</tr>
<%
}
}
%>
</table>
</section>
<footer>
<address>Copyright @2018 All right reserved Coffee House</address>
</footer>
</body>
</html>
4) shopList.jsp
<%@page import="net.coffee.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="java.util.*"%>
<%@ page import="java.util.ArrayList"%>
<%@ page import="net.utility.*" %>
<jsp:useBean id="dao" class="net.coffee.CoffeeDAO"></jsp:useBean>
<jsp:useBean id="dto" class="net.coffee.ShopDTO"></jsp:useBean>
<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>매장별 커피 판매관리</title>
</head>
<body>
<header>
<h1>매장별 커피 판매관리 프로그램</h1>
<div class="nav">
<jsp:include page="mainMenu.jsp"></jsp:include>
</div>
</header>
<section>
<h2>매장별 판매액</h2>
<table border='1'>
<tr>
<th>매장코드</th>
<th>매장명</th>
<th>매장별 판매액</th>
</tr>
<%
ArrayList<ShopDTO> list = dao.shoplist();
if (list == null) {
out.println("<tr>");
out.println(" <td colspan='4'>결과를 찾을 수 없습니다.</td>");
out.println("</tr>");
} else {
for (int i = 0; i < list.size(); i++) {
dto = list.get(i);
%>
<tr>
<td><%=dto.getScode()%></td>
<td><%=dto.getSname()%></td>
<td><%=Utility.comma(dto.getShap())%></td>
</tr>
<%
}
}
%>
</table>
</section>
<footer>
<address>Copyright @2018 All right reserved Coffee House</address>
</footer>
</body>
</html>
5) proList.jsp
<%@page import="net.coffee.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="java.util.*"%>
<%@ page import="java.util.ArrayList"%>
<%@ page import="net.utility.*" %>
<jsp:useBean id="dao" class="net.coffee.CoffeeDAO"></jsp:useBean>
<jsp:useBean id="dto" class="net.coffee.ProDTO"></jsp:useBean>
<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>매장별 커피 판매관리</title>
</head>
<body>
<header>
<h1>매장별 커피 판매관리 프로그램</h1>
<div class="nav">
<jsp:include page="mainMenu.jsp"></jsp:include>
</div>
</header>
<section>
<h2>매장별 판매액</h2>
<table border='1'>
<tr>
<th>상품코드</th>
<th>상품명</th>
<th>매장별 판매액</th>
</tr>
<%
ArrayList<ProDTO> list = dao.prolist();
if (list == null) {
out.println("<tr>");
out.println(" <td colspan='4'>결과를 찾을 수 없습니다.</td>");
out.println("</tr>");
} else {
for (int i = 0; i < list.size(); i++) {
dto = list.get(i);
%>
<tr>
<td><%=dto.getPcode()%></td>
<td><%=dto.getName()%></td>
<td><%=Utility.comma(dto.getHap())%></td>
</tr>
<%
}
}
%>
</table>
</section>
<footer>
<address>Copyright @2018 All right reserved Coffee House</address>
</footer>
</body>
</html>
4. form 양식 & insert 기능
1) saleForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>판매등록</title>
</head>
<body>
<header>
<h1>매장별 커피 판매관리 프로그램</h1>
<div class="nav">
<jsp:include page="mainMenu.jsp"></jsp:include>
</div>
</header>
<div class="wrap" style="margin: 0 auto;">
<h2>판매등록</h2>
<form action="saleIns.jsp" method="post" name="salefrm">
<table border='1'>
<tr>
<th>비번호</th>
<td><input type="text" name="saleno" id="saleno">
</td>
</tr>
<tr>
<th>상품코드</th>
<td><input type="text" name="pcode" id="pcode" required>
</td>
</tr>
<tr>
<th>판매날짜</th>
<td><input type="date" name="saledate" id="saledate">
</td>
</tr>
<tr>
<th>매장코드</th>
<td><input type="text" name="scode" id="scode">
</td>
</tr>
<tr>
<th>판매수량</th>
<td><input type="number" name="amount" id="amount" required>
</td>
</tr>
<tr>
<td colspan='2'>
<button type="submit">입력하기</button>
<button type="reset">다시쓰기</button>
</td>
</tr>
</table>
</form>
</div>
<footer>
<address>Copyright @2018 All right reserved Coffee House</address>
</footer>
</body>
</html>
2) saleIns.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="java.util.*"%>
<%@ page import="java.util.ArrayList"%>
<%@ page import="net.coffee.*"%>
<jsp:useBean id="dao" class="net.coffee.CoffeeDAO"></jsp:useBean>
<jsp:useBean id="dto" class="net.coffee.SaleDTO"></jsp:useBean>
<%
request.setCharacterEncoding("UTF-8");
int saleno = Integer.parseInt(request.getParameter("saleno").trim());
String pcode = request.getParameter("pcode").trim();
String scode = request.getParameter("scode").trim();
int amount = Integer.parseInt(request.getParameter("amount").trim());
dto.setSaleno(saleno);
dto.setPcode(pcode);
dto.setScode(scode);
dto.setAmount(amount);
int cnt = dao.insert(dto);
if (cnt == 0){
out.println("실행되지 않음");
} else {
out.println("<script>");
out.println(" alert('상품이 등록되었습니다.');");
out.println(" location.href='saleList.jsp';");
out.println("</script>");
}
%>
5. DTO작성 및 DAO 기능연결
뷰페이지별로 조인하고 조회한 내용이 다르기 때문에 여러 DTO를 작성하였다.
조회 내용 별 SQL코드
-- 판매현황 (saleList)
select TSALE.saleno ,TSALE.pcode,TSALE.saledate,TSALE.scode,tpro.name,
TSALE.amount, (TPRO.cost * tsale.amount) as TOT
from tbl_salelist_01 TSALE join tbl_product_01 TPRO
on TSALE.pcode = TPRO.pcode;
-- 매장별 판매액 (shopList)
select AA.scode, TSH.sname, AA.SHAP from
(select TSALE.scode, SUM(TPRO.cost * tsale.amount) as SHAP
from tbl_salelist_01 TSALE join tbl_product_01 TPRO
on TSALE.pcode = TPRO.pcode
GROUP BY TSALE.scode)AA join tbl_shop_01 TSH
on AA.scode = TSH.scode;
-- 상품별 판매액 (proList)
select TPRO.pcode, TPRO.name, sum(TPRO.cost * tsale.amount) as HAP
from tbl_product_01 TPRO join tbl_salelist_01 TSALE
on TSALE.pcode = TPRO.pcode
GROUP BY TPRO.pcode, TPRO.name
ORDER BY TPRO.pcode ASC;
DTO 작성
1) ProDTO
package net.coffee;
public class ProDTO {
private String scode;
private String sname;
private String pcode;
private String name;
private int cost;
private int amount;
private int hap;
public ProDTO() {
}
public String getScode() {
return scode;
}
public void setScode(String scode) {
this.scode = scode;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getPcode() {
return pcode;
}
public void setPcode(String pcode) {
this.pcode = pcode;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public int getHap() {
return hap;
}
public void setHap(int hap) {
this.hap = hap;
}
@Override
public String toString() {
return "ProDTO [scode=" + scode + ", sname=" + sname + ", pcode="
+ pcode + ", name=" + name + ", cost=" + cost + ", amount="
+ amount + ", hap=" + hap + "]";
}
}
2) SaleDTO
package net.coffee;
public class SaleDTO {
private int saleno;
private String pcode;
private String saledate;
private String scode;
private String name;
private int amount;
private int tot;
public SaleDTO() {
}
public int getSaleno() {
return saleno;
}
public void setSaleno(int saleno) {
this.saleno = saleno;
}
public String getPcode() {
return pcode;
}
public void setPcode(String pcode) {
this.pcode = pcode;
}
public String getSaledate() {
return saledate;
}
public void setSaledate(String saledate) {
this.saledate = saledate;
}
public String getScode() {
return scode;
}
public void setScode(String scode) {
this.scode = scode;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public int getTot() {
return tot;
}
public void setTot(int tot) {
this.tot = tot;
}
@Override
public String toString() {
return "SaleDTO [saleno=" + saleno + ", pcode=" + pcode + ", saledate="
+ saledate + ", scode=" + scode + ", name=" + name
+ ", amount=" + amount + ", tot=" + tot + "]";
}
}
3) ShopDTO
package net.coffee;
public class ShopDTO {
private String scode;
private String sname;
private String pcode;
private int cost;
private int amount;
private int shap;
public ShopDTO() {
}
public String getScode() {
return scode;
}
public void setScode(String scode) {
this.scode = scode;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getPcode() {
return pcode;
}
public void setPcode(String pcode) {
this.pcode = pcode;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public int getShap() {
return shap;
}
public void setShap(int shap) {
this.shap = shap;
}
@Override
public String toString() {
return "ShopDTO [scode=" + scode + ", sname=" + sname + ", pcode="
+ pcode + ", cost=" + cost + ", amount=" + amount + ", shap="
+ shap + "]";
}
}
DAO 작성
조회와 데이터 삽입 기능이 있는 DAO
package net.coffee;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import DBPKG.DBOpen;
public class CoffeeDAO {
//1) 판매현황
public ArrayList<SaleDTO> salelist(){
ArrayList<SaleDTO> list=null;
try{
Connection con=DBOpen.getConnection();
StringBuilder sql=new StringBuilder();
sql.append(" select TSALE.saleno ,TSALE.pcode, TSALE.saledate, TSALE.scode, TPRO.name, TSALE.amount, (TPRO.cost * TSALE.amount) as TOT ");
sql.append(" from ");
sql.append(" tbl_salelist_01 TSALE join tbl_product_01 TPRO ");
sql.append(" on TSALE.pcode = TPRO.pcode ");
sql.append(" ORDER BY TSALE.saleno ");
PreparedStatement pstmt=con.prepareStatement(sql.toString());
ResultSet rs=pstmt.executeQuery();
if(rs.next()){
list=new ArrayList<SaleDTO>();
do{
SaleDTO dto=new SaleDTO();
dto.setSaleno(rs.getInt("saleno"));
dto.setPcode(rs.getString("pcode"));
dto.setSaledate(rs.getString("saledate"));
dto.setScode(rs.getString("scode"));
dto.setName(rs.getString("name"));
dto.setAmount(rs.getInt("amount"));
dto.setTot(rs.getInt("tot"));
list.add(dto);
}while(rs.next());
}else{
list=null;
}//if end
}catch(Exception e){
System.out.println("판매현황조회실패:"+e);
}//try end
return list;
}//list() end
//2) 매장별 판매액
public ArrayList<ShopDTO> shoplist(){
ArrayList<ShopDTO> list=null;
try{
Connection con=DBOpen.getConnection();
StringBuilder sql=new StringBuilder();
sql.append(" select AA.scode, TSH.sname, AA.SHAP ");
sql.append(" from ( ");
sql.append(" SELECT TSALE.scode, SUM(TPRO.cost * tsale.amount) as SHAP ");
sql.append(" from tbl_salelist_01 TSALE join tbl_product_01 TPRO ");
sql.append(" on TSALE.pcode = TPRO.pcode ");
sql.append(" GROUP BY TSALE.scode ) AA join tbl_shop_01 TSH ");
sql.append(" on AA.scode = TSH.scode ");
PreparedStatement pstmt=con.prepareStatement(sql.toString());
ResultSet rs=pstmt.executeQuery();
if(rs.next()){
list=new ArrayList<ShopDTO>();
do{
ShopDTO dto=new ShopDTO();
dto.setScode(rs.getString("scode"));
dto.setSname(rs.getString("sname"));
dto.setShap(rs.getInt("shap"));
list.add(dto);
}while(rs.next());
}else{
list=null;
}//if end
}catch(Exception e){
System.out.println("매장별 판매액 조회실패:"+e);
}//try end
return list;
}//list() end
//3) 상품별 판매액
public ArrayList<ProDTO> prolist(){
ArrayList<ProDTO> list=null;
try{
Connection con=DBOpen.getConnection();
StringBuilder sql=new StringBuilder();
sql.append(" select TPRO.pcode, TPRO.name, sum(TPRO.cost * tsale.amount) as HAP ");
sql.append(" from tbl_product_01 TPRO join tbl_salelist_01 TSALE ");
sql.append(" on TSALE.pcode = TPRO.pcode ");
sql.append(" GROUP BY TPRO.pcode, TPRO.name ");
sql.append(" ORDER BY TPRO.pcode ASC ");
PreparedStatement pstmt=con.prepareStatement(sql.toString());
ResultSet rs=pstmt.executeQuery();
if(rs.next()){
list=new ArrayList<ProDTO>();
do{
ProDTO dto=new ProDTO();
dto.setPcode(rs.getString("pcode"));
dto.setName(rs.getString("name"));
dto.setHap(rs.getInt("hap"));
list.add(dto);
}while(rs.next());
}else{
list=null;
}//if end
}catch(Exception e){
System.out.println("매장별 판매액 조회실패:"+e);
}//try end
return list;
}//list() end
//4) 판매등록
//1) Data Insert
public int insert(SaleDTO dto) {
int cnt = 0;
try {
//1. DB연결
Connection con=DBOpen.getConnection();
StringBuilder sql = new StringBuilder();
//2. Insert SQL문 작성
sql.append(" INSERT INTO tbl_salelist_01 ( saleno, pcode, saledate, scode, amount ) ");
sql.append(" VALUES( ");
sql.append(" ?,?,SYSDATE,?,? ) ");
//3. SQL문 변환
PreparedStatement pstmt = con.prepareStatement(sql.toString());
//4. Value값 불러오기
pstmt.setInt(1, dto.getSaleno());
pstmt.setString(2, dto.getPcode());
pstmt.setString(3, dto.getScode());
pstmt.setInt(4, dto.getAmount());
//5. 실행
cnt = pstmt.executeUpdate();
} catch(Exception e) {
System.out.println("행추가일때: "+e);
}
return cnt;
}
}
5. 결과
1) 판매등록
2) 판매현황
3) 매장별판매액
4) 상품별판매액
5) 홈
'Backend' 카테고리의 다른 글
[python] 파이썬 클린코드 (1) (0) | 2021.05.07 |
---|---|
코드로 배우는 Spring Web 프로젝트 Ⅰ (0) | 2019.12.02 |
10월 18일 금 | SPRING 04 - 스프링 프로젝트 mymelon (0) | 2019.10.18 |
10월 16일 수 | SPRING 02 - 스프링 프로젝트 MAVEN (0) | 2019.10.15 |
10월 14일 월 | SPRING 01 - 스프링 프로젝트 시작하기 (0) | 2019.10.14 |
댓글