Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
Tags
- 바인딩변수
- jointpoint
- after-throwing
- @RequestParam
- application.properties
- 비즈니스레이어
- frontController
- @ResponseBody
- Java
- springjdbc
- AOP
- 어노테이션
- 유효성검사
- 스프링
- PointCut
- produces
- 생성자주입
- springmvc
- .xml
- @
- 의존주입
- Model
- SpringBoot
- MVC
- @RequestMapping
- c:if
- 서비스레이어
- gradle
- spring
- @Valid
Archives
- Today
- Total
메모장
[실습문제] 회원 상품구매 MVC패턴 본문
728x90
반응형
사용자가 회원가입을 진행후 로그인 하여야 상품을 구매할 수 있다. MVC패턴을 활용하여 작성해보자 !
- 관리자의 회원 목록 출력
- 사용자의 회원가입
- 사용자 상품목록 출력
- 사용자 상품 이름으로 검색
- 사용자 상품 가격으로 검색
- 사용자 상품_최고가 검색
- 사용자 상품_최저가 검색
위 목록 들만 일단 로직을 작성해보자 !
// 공부를 위해서 주석이 많다...
Model - MemberVO
package model;
// 모든 데이터는 반드시 가져야 하는 속성 : 식별가능 해야함 PK 가 있어야함
public class MemberVO {
private String mid; // 아이디 PK
// : 일반적으로 PK는 시스템에서 부여한다
// : 사용자가 등록 -----> 회원가입이기 때문에 ☆ 중복검사 필수 ☆
private String mpw; // 회원 비밀번호
private String name; // 회원 이름
public MemberVO(String mid,String mpw,String name) {
this.mid=mid;
this.mpw=mpw;
this.name=name;
}
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public String getMpw() {
return mpw;
}
public void setMpw(String mpw) {
this.mpw = mpw;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return this.name+"님";
}
@Override
public boolean equals(Object obj) { // equals 오버라이딩 하기
// 아이디(mid) 가 같으면 같다라고 할 예정 , 같은객체 !
MemberVO mVO=(MemberVO)obj; // 다운 캐스팅
if(this.mid.equals(mVO.mid)) { // 내가 가진 아이디와 받아온 새로운 아이디가 같니 ?
return true; // 그러면 중복이야
}
return false; // 아니야 중복 아니야
}
}
Model - MemberDAO
package model;
import java.util.ArrayList;
// 모델에서 작성해야 하는 것 : 구현할 기능
// 회원목록출력
// 회원가입(C), 로그인(R), 로그아웃(R), 마이페이지(U), 회원탈퇴(D)
public class MemberDAO {
private ArrayList<MemberVO> datas;
public MemberDAO() {
datas=new ArrayList<MemberVO>();
datas.add(new MemberVO("admin","1234","관리자")); // 아이디, 비밀번호,
datas.add(new MemberVO("coding_helper","timo","작은 티모"));
}
public ArrayList<MemberVO> selectAll(MemberVO mVO){ //선택한 전체
return datas; // 내가 가진 현재 회원목록 반환 하면 된다
}
public MemberVO selectOne(MemberVO mVO){
return null;
}
public boolean insert(MemberVO mVO) { // C -> M 정보로 회원가입 가능한가요 ?라고 보내줬다
// 한글코딩
//mVO 가 의미하는것 == 회원가입할 정보 <--- 로직에 들어갈 내용 //mVO -> 나 회원가입 시켜줘
//mVO를 내가가진 datas 에 넣어줄 예정
//PK 중복검사를 해야하고, 성공하면 add()
// 회원가입 시켜주세요 ~~ 하면 -> 일단 아이디 중복 검사 부터 해
// 내가 갖고있는걸 이거인지 저거인지 다 ~~~ 살펴봐야 아는 상황 --> flag 로직을 써야한다
// if(혹시 PK가 중복되었어?) {
// return false; //실패하면 false 반환
// }
// datas에 mVO정보를 저장 add() // 중복검사가 통과가 되면
// return true;
boolean flag = false; // 처음부터 끝까지 돌면서 나와같은 아이디가 있는지 체크하겠습니다
for(MemberVO data : datas) {
// 아이디가 같은지 볼려면 데이터를 다 봐야한다. for문으로 데이터 다 ~ 보기
if(data.equals(mVO)) { //객체비교는 equals //오버라이딩 하러 가자
//data == mVO<--- 객체비교는 연산자로 할수 없음 !!
// 하나라도 회원가입한 정보랑 같아 ?
flag = true; // 그러면 flag = true 이다
System.out.println(" 로그: MemberDAO: insert(): 아이디 중복"); // 로그 달아줌
break;
}
}
if(flag) { // 중복검사를 일단 하고 // PK가 중복 되었어?
return false; // 그러면 중복된 아이디야
}
//if를 들어가지 않아 잘 내려왔다면
//datas 에 새로운 사람을 추가 하게 되고 // 새로운 아이디, 새로운 비번, 새로운 닉네임
datas.add(new MemberVO(mVO.getMid(),mVO.getMpw(),mVO.getName()));
// 객체 new 써주고 객체
//새로운 사람을 만들 적에(인자였던 mVO에서 추출);
return true;
}
// 한글코딩
//datas 에 mVO정보를 저장 add() // 중복검사 성공하면 저장을 해
//mVO 를 datas 에 넣어줄 예정
// 인자였던 mVO 순으로 꺼내면 됨
/* 51 번라인 설명
*1) datas.add(new MemberVO());
* -> datas DB공간에 new 새로운 데이터를 추가 // new 객체
*2) MemberVO(데이터1,데이터2,데이터3)
-> 데이터들을 mVO에서 추출하면됨
*/
public boolean update(MemberVO mVO) {
return true;
}
public boolean delete(MemberVO mVO) {
return true;
}
}
Model - ProductVO
package model;
public class ProductVO {
private int num; // 상품번호 PK
private String name;
private int price;
private int cnt; // 조회수 // 검색을 당할때마다 조회수가 올라가도록 코딩 예정
public ProductVO(int num,String name,int price,int cnt) {
this.num=num;
this.name=name;
this.price=price;
this.cnt=cnt;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
@Override
public String toString() { // toString 재정의 했다.
String msg=num+". "+name+" "+price+"원 [" + cnt + "]";
return msg;
}
}
Model - ProductDAO
package model;
import java.util.ArrayList;
// 목록출력,목록검색_이름,목록검색_가격순,목록검색_조회수
public class ProductDAO {
private ArrayList<ProductVO> datas;
private static int PK=1001;
public ProductDAO() {
datas=new ArrayList<ProductVO>();
datas.add(new ProductVO(PK++,"장갑",12000,2));
datas.add(new ProductVO(PK++,"화장지",1000,1));
datas.add(new ProductVO(PK++,"장화",23000,0));
datas.add(new ProductVO(PK++,"지갑",120000,1));
datas.add(new ProductVO(PK++,"화분",5500,2));
}
public ArrayList<ProductVO> selectAll(ProductVO pVO){// 내가 가진 상품 반환
// 높은 응집도를 위해
// 여러기능을 하나의 메서드에서 작성
if(pVO==null) {// pVO가 null 이야 ? 그러면 원래 기능이야
return datas;
}
else if(pVO.getName().equals("필터검색")) { //이거 필터검색이야 ? //searchCondition
ArrayList<ProductVO> mdatas=new ArrayList<ProductVO>();//반환해줄 배열 만들고
//가격범위에 들어가는 데이터들을 추가해서
for(ProductVO data : datas) {
if(pVO.getPrice() <= data.getPrice() && data.getPrice() <= pVO.getCnt()) {
//가격범위에 들어가는 데이터야 ?
// 최저 <= 내 가격 && 내가격 >= 최고
mdatas.add(data);
}
}
return mdatas;//반환해
}
else {// 상품 목록 이니까 Product -> 그리고 검색이니까 전체를 보고 검사를 해줘야 한다. 상품 검색
// null이 아니면 ? 이름으로 검색
// '상품목록출력'을 할때에는 인자(pVO)가 null이므로
// pVO가 null이 아니라면 '이름으로검색'임!
ArrayList<ProductVO> mdatas=new ArrayList<ProductVO>();
for(ProductVO data:datas) {
if(data.getName().contains(pVO.getName())) {
// 처음부터 끝까지 돌면서
// if(사용자가 입력한 이름을 가진 데이터야)?{
// // () 내용 : 현재 보고 있는 데이터 이름(data.getName)이 검색어(pVO.getName)를 포함하고(contains) 있어 ?
// // pVO.getName() == 사용자가 검색한 단어
mdatas.add(data);
}
}
return mdatas;
}
// 상품 이름으로 검색에 대한 한글 코딩
// if(이름으로 검색중이니?) { // pVO != null
// // pVO가 null이 아니라면 '이름으로 검색'임
// 처음부터 끝까지 돌면서
// if(사용자가 입력한 이름을 가진 데이터야)?{
// () 내용 : 현재 보고 있는 데이터 이름(data.getName)이
// 검색어(pVO.getName)를 포함하고(contains) 있어 ?
// pVO.getName() == 사용자가 검색한 단어
// (mdatas)output에 저장해
// mdatas.add(data);
// }
// return (mdatas)output하자!
// }else {
// return datas;
// }
}
public ProductVO selectOne(ProductVO pVO){ //하나를 고를 때
//가장 비싼 상품 = pVO.getName;
if(pVO.getName().equals("최고가") ) {// searchCondition //지금 "최고가" 검색이야 ?
//최고를 찾는 알고리즘
int max=datas.get(0).getPrice(); // 최대값이 0번 인덱스라고 설정해놓기
int maxIndex=0; // 최고값 인덱스 번호 찾아내기
for(int i=1;i<datas.size();i++) { // 배열 전체를 돌면서
if(max<datas.get(i).getPrice()) { // 최고값보다 혹시 큰 값이 나타나면?
max=datas.get(i).getPrice(); // 그 인덱스번호자리의 숫자가 최고값이 된다
maxIndex=i; // 그때의 최고값 인덱스 번호
}
}
return datas.get(maxIndex); // 최고가 상품 반환
}else if(pVO.getName().equals("최저가")) { //너가 입력한 그 이름이 혹시 "최저가"이니?
int min = datas.get(0).getCnt(); //최소값은 인덱스0번에 있다고 가정한다.
int minIndex = 0; // 인덱스 번호 알기
for (int i = 0; i < datas.size(); i++) { // 배열 전체를 돌 동안
if(min > datas.get(0).getCnt()) { // 최저가 보다 작은값이 있다면
min = datas.get(i).getCnt(); // 그 인덱스 번호의 값이 최저값이 된다.
minIndex=i; // 최저 인덱스 번호
}
}
return datas.get(minIndex); // 최저가 상품 반환
}
return null;
}
public boolean insert(ProductVO pVO) {
return true;
}
public boolean update(ProductVO pVO) {
return true;
}
public boolean delete(ProductVO pVO) {
return true;
}
}
View - AdminView
package view;
import java.util.ArrayList;
import java.util.Scanner;
import model.MemberVO;
// 회원목록출력
public class AdminView {
private static Scanner sc=new Scanner(System.in);
public int printAdminMenu() {
System.out.println();
System.out.println("=== 관 리 자 모 드 ===");
System.out.println("1. 회원목록출력");
System.out.println("2. 관리자모드 종료");
System.out.print("입력) ");
return sc.nextInt();
}
public void printMemberList(ArrayList<MemberVO> mdatas) { // mdatas정보를 가져온다
System.out.println();
if(mdatas.isEmpty()) { // 혹시 정보가 비어있니 ?
System.out.println("=== 가입한 회원이 없습니다 ===");
return;
}
System.out.println("=== 회 원 목 록 ===");
for(MemberVO mdata:mdatas) {
System.out.println(mdata);
}
}
public void printAdminEnd() {
System.out.println();
System.out.println("=== 관 리 자 모 드 종 료 ===");
}
}
View - ClientView
package view;
import java.util.ArrayList;
import java.util.Scanner;
import model.MemberVO;
import model.ProductVO;
// 회원가입C,로그인R,로그아웃R,마이페이지U,회원탈퇴D
// 목록출력,목록검색_이름,목록검색_가격
public class ClientView {
private static Scanner sc=new Scanner(System.in);
public int printClientMenu01() { // 로그인 모드
// 로그인 하지 않은 사용자는 상품을 구매할 수 없다.
System.out.println();
System.out.println("=== 프 로 그 램 모 드 ===");
System.out.println("1. 회원가입");
System.out.println("2. 로그인");
System.out.println("3. 프로그램 종료");
System.out.print("입력) ");
return sc.nextInt();
}
public MemberVO signUp() { // 회원가입할 시에 안내 해줘야 하는것
System.out.println();
System.out.println("=== 회 원 가 입 ===");
System.out.print("아이디입력) ");
String mid=sc.next();
System.out.print("비밀번호입력) ");
String mpw=sc.next();
System.out.print("이름입력) ");
String name=sc.next(); // 아이디 비밀번호 이름 까지 입력해서
return new MemberVO(mid,mpw,name); // 그것을 전달 하는것 까지
// vo는 단순 자료형이다 ~ view에서 활용가능, DAO는 view에서 호출 안됨
}
public void signUpTrue() {
System.out.println();
System.out.println("회원가입 성공!");
}
public void signUpFalse() {
System.out.println();
System.out.println("회원가입 실패...");
}
public MemberVO signIn() {
System.out.println();
System.out.println("=== 로 그 인 ===");
System.out.print("아이디입력) ");
String mid=sc.next();
System.out.print("비밀번호입력) ");
String mpw=sc.next();
return new MemberVO(mid,mpw,null); // 아이디랑 비밀번호를 입력 받는다.
}
public void signInTrue() {
System.out.println();
System.out.println("로그인 성공!");
}
public void signInFalse() {
System.out.println();
System.out.println("로그인 실패...");
}
public void printClientEnd01() {
System.out.println();
System.out.println("=== 프 로 그 램 종 료 ===");
}
public int printClientMenu02() { // 로그인 하면 들어오는 상태
System.out.println();
System.out.println("=== 사 용 자 모 드 ===");
System.out.println("1. 로그아웃(사용자모드 종료)");
System.out.println("2. 마이페이지");
System.out.println("3. 회원탈퇴(사용자모드 종료)");
System.out.println("4. 상품목록출력");
System.out.println("5. 상품목록검색_이름으로 검색");
System.out.println("6. 상품목록검색_가격으로 검색");
System.out.println("7. 상품검색_최고가 검색"); // 하나를 검색
System.out.println("8. 상품검색_최저가 검색");
System.out.print("입력) ");
return sc.nextInt();
}
public ProductVO getSearchFilter() { // 최저부터 최고 까지 두개를 입력받음
System.out.println();
System.out.println("=== 가 격 으 로 검 색 ===");
System.out.print("최저가격입력) ");
int minPrice = sc.nextInt();
System.out.print("최고가격입력) ");
int maxPrice = sc.nextInt();
// 인자가 두개를 반환하는 행위는 못하기 때문에
//pVO에 담아서 넘겨준다.
// 안쓰는 공간을 활용하여 minPrice랑 maxPrice 를 두개 한번에 넘겨준다.
ProductVO pVO=new ProductVO(0,"필터검색",minPrice,maxPrice);
// "필터검색" 으로 C 과 M 을 이어줄 예정
// searchCondition
return pVO;
}
public String getSearchContent() {// 그렇다면 검색어 넣으렴
System.out.println();
System.out.println("=== 이 름 으 로 검 색 ===");
System.out.print("검색어입력) ");
String name=sc.next();
return name;
}
public void printProduct(ProductVO pVO) {
System.out.println();
if(pVO==null) {
System.out.println("=== 출 력 할 상 품 없 음 ===");
return;
}
System.out.println("=== 상 품 출 력 ===");
System.out.println(pVO);
}
public void printProductList(ArrayList<ProductVO> pdatas) {
//내가 목록을 출력하고 싶은 사람이야 //목록이 있어야 보여줄것 아니냐 목록을 받아야 보여준다.
System.out.println();
if(pdatas.isEmpty()) { // 혹시 비어있니 ? // 사용자편의성고려(UI/UX)
System.out.println("=== 출 력 할 상 품 없 음 ===");
return;
}
System.out.println("=== 상 품 목 록 출 력 ===");
for(ProductVO data:pdatas) {
System.out.println(data); // .toString()은 자동호출됩니다!
}
}
public void logout() {
System.out.println();
System.out.println("=== 로 그 아 웃 ===");
}
public void printClientEnd02() {
System.out.println();
System.out.println("=== 사 용 자 모 드 종 료 ===");
}
}
Controller - ProductController
package ctrl;
import java.util.ArrayList;
import model.MemberDAO;
import model.MemberVO;
import model.ProductDAO;
import model.ProductVO;
import view.AdminView;
import view.ClientView;
public class Ctrl {
private MemberDAO mDAO; // 회원가입한 사용자
private ProductDAO pDAO; // 상품
private AdminView admin;
private ClientView client;
public Ctrl() {
mDAO=new MemberDAO();
pDAO=new ProductDAO();
admin=new AdminView();
client=new ClientView();
}
public void startApp() {
while(true) {
int action=client.printClientMenu01();
if(action==1234) { // 관리자 모드 진행 1234야?
while(true) { // 관리자 모드 진입
action=admin.printAdminMenu();
if(action==1) { // 회원목록 출력
//1. 사용자가 관리자인 상황
//if(action==1) // 2. 관리자가 회원목록볼래~
//model.selectAll(); // 모델.회원목록줘(); //3,4,5,6번의 내용
// 3. V가 C한테 말합니다. 회원목록보겠다는데?
// 4. C가 아, 회원목록데이터가 필요하구나? ㅇㅋ
// 5. M한테 회원목록 좀 줘~
// 6. M이 selectAll()합니다.
ArrayList<MemberVO> mdatas=mDAO.selectAll(null); //selectAll하려면
//인자가 있어야 하는데 넘겨줄 인자가 우리에겐 없기 때문에 그냥 null로 한다.
// 7. AL<MVO>가 반환되고 C는 그걸-
//view.회원 목록 출력(datas);
admin.printMemberList(mdatas); // view 목록
// 8. -V한테 줍니다.
// 줄인 코드도 참고
//admin.printMemberList(mDAO.selectAll(null));
}
else if(action==2) { // 관리자 모드 종료
admin.printAdminEnd();
break;
}
}
}
else if(action==1) { // 회원가입
// int action=client.printClientMenu01();
// 1. 사용자가 CONSOLE에 회원가입 할래 1번 입력
//2. V는 입력한 값을 C한테 전달 -> 전달된값 action
//else if(action==1)
// 3. C는 아 회원가입 하고 싶구나 ? 알겠어 ~
// 아이디,비밀번호, 이름 입력까지 해서 그것을 전달 하는 것 까지
MemberVO mVO = client.signUp(); // 정보들을 mVO에 담는다.
//회원가입 정보 = view.회원가입화면(); //VIEW야! 회원가입화면(); 띄워야지
// 그러면 회원가입 정보를 받아와야지
//4.C는 V한테 회원가입할 정보를 입력받아오라고 지시 // client.signup
//5.V는 사용자한테 회원가입 창 보여줌 // 회원가입 입력을 받도록 세팅
//6.사용자는 정보를 입력 // 사용자가 입력을 완료
//7.V는 사용자가 입력한 정보를 C한테 전달 signup에서 수행한 것들 mVO에 담음
// 회원가입 정보
// 회원가입이니까 mDAO가 맞다.
boolean flag = mDAO.insert(mVO); // true로 해놓은 상태
// 회원가입이니까 mDAO와 pDAO 둘중 ---> mDAO가 맞다.
//boolean 성공실패여부 = mDAO.insert(mVO);
//8.C는 이 정보를 M한테 전달 // model아 회원가입 정보좀 줘야겠다.
// model.회원가입(mVO); // 회원가입은 == insert에 해당
//9.M은 회원가입을 수행 ---> model insert쪽에서 기능 수행
// boolean 성공실패 여부 = model.insert(a);
//10.M은 insert()가 잘 되었는지 아닌지를 T,F 반환 ---> boolean
if(flag) {//성공했어 ?
client.signUpTrue();//view.성공화면();
}else {
client.signInFalse();// view.실패화면();
}
//11.C는 결과에 따라 맞는 V화면을 보여줄수 있도록 지시
//12.사용자는 V가 보여주는 화면에 보게됨
// 코드를 줄이면 이렇게 변함
// if(mDAO.insert(client.signUp())) {
// client.signUpTrue();
// }
// else {
// client.signUpFalse();
// }
}
else if(action==2) { // 로그인
if(mDAO.selectOne(client.signIn())!=null) { // 로그인을 정상적으로 진행 했다면
client.signInTrue();
while(true) { // 로그인을 정상적으로 진행 됐다면 // 사용자 모드로 진입
action=client.printClientMenu02();
if(action==1) { // 로그아웃
client.logout();
client.printClientEnd02();
break;
}
else if(action==2) { // 마이페이지
}
else if(action==3) { // 회원탈퇴
client.printClientEnd02();
break;
}
else if(action==4) { // 상품목록출력 // 상품은 그냥 보여주기만 하면됨
// M에서 받아서 C 출력
ArrayList<ProductVO> pdatas=pDAO.selectAll(null);
client.printProductList(pdatas); // 회원목록출력이랑 맥락이 같다.
}
else if(action==5) { // 상품목록검색_ 이름으로 검색
// 검색은 상품의 이름을 입력받아서 보여줘야 된다는 차이점이 있다.
String name = client.getSearchContent();
// 검색어를 뷰로 부터 받아오기
// 검색어를 뭘 받아올래 ?
// 그 검색어를 모델 한테 넘겨주기
ProductVO pVO = new ProductVO (0,name,0,0);
// 우리는 이름만 필요하니까
pDAO.selectAll(pVO);
ArrayList<ProductVO> pdatas = pDAO.selectAll(pVO);
client.printProductList(pdatas);
}
else if(action==6) { // 상품목록검색_가격으로 검색
//사용자가 V에서 검색을 하고싶다고 말하면서 버튼을 누른다.
// 무엇을 검색으로 받아올래 ? price
// C가 V한테 검색할 것을 받아오라고 한다
// V는 사용자에게 검색에 대해 가격을 입력받아온다.
// V가 가격을 받아와 C에게 전달한다.
ProductVO pVO=client.getSearchFilter();
// C는 M에게 넘겨서 이 가격에 대해 가장 비싼 상품검색해줘라고 전달한다
pDAO.selectAll(pVO);
// M은 가격을 입력받아 그 가격에 대한 정보를 검색한다.
// M에서 검색한 정보를 C에게 전달한다.
ArrayList<ProductVO> pdatas=pDAO.selectAll(pVO);
// C는 V에게 검색한 정보를 전달한다.
// V는 사용자에게 보여준다.
client.printProductList(pdatas);
// C는 View 검색완료 안내멘트를 띄운다.
}
else if(action==7) { // 상품검색_최고가 검색
ProductVO pVO=new ProductVO(0,"최고가",0,0);
ProductVO data=pDAO.selectOne(pVO);
client.printProduct(data);
}
else if(action==8) { // 상품검색_최저가 검색
ProductVO pVO = new ProductVO(0,"최저가",0,0);
ProductVO data = pDAO.selectOne(pVO);
client.printProduct(data);
}
}
}
else { // 로그인에 성공하지 못했다면 실패 메세지
client.signInFalse();
}
}
else if(action==3) { // 프로그램 종료
client.printClientEnd01();
break;
}
}
}
}
728x90
반응형
'JAVA > [실습문제]' 카테고리의 다른 글
| [실습 문제] 지니뮤직 웹크롤링 해보기 ! (1) | 2024.04.08 |
|---|---|
| [실습문제] 회원 상품구매 MVC패턴 2 (0) | 2024.04.08 |
| [실습문제] 자판기프로그램 MVC패턴 (0) | 2024.04.08 |
| [실습문제] 포켓몬(추상클래스, private) (0) | 2024.04.08 |
| [실습문제] 상속 (응용문제) (0) | 2024.04.08 |