//프로그램 시작과 종료
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
System.out.println("== 프로그램 시작 ==");
Scanner sc = new Scanner(System.in);
int lastArticleId = 0; // 작성되는 게시글들을 1번부터 발급.
int lastMemberId = 0; // 작성되는 회원번호는 1번부터 발급.
List<Article> articles = new ArrayList<>(); //게시글을 담을 상자
List<Member> members = new ArrayList<>(); // 회원 정보를 담을 상자
//List 제너릭 괄호 기호 오류, <> 로 변경해야 한다.
while (true) {
System.out.print("명령어 ) ");
String cmd = sc.nextLine().trim();
//컴퓨터는 같은 단어여도 공백 유무에 따라 다르게 판단한다.
// 따라서 실수 없이 같은 값을 보여주기 위해 앞 뒤 공백을 제거해주는 trim();을 사용해야 한다.
//문자열이 필요하므로 split()은 불필요하다. 사용시에는 ()안에 기준값 넣어야 한다.
if (cmd.length() == 0) {
System.out.println("명령어를 입력해주세요");
// 아무것도 입력하지 않았을 때의 출력문은 맨 위로 올리는 것이 좋다.
continue;
}
if (cmd.equals("exit")) {
break; // 프로그래밍 종료
}
// =======================================================
// [1] 회원 가입 (member join) - 새로 추가된 기능
// =======================================================
else if (cmd.equals("member join")) {
System.out.println("== 회원 가입 ==");
String loginId;
String loginPw;
String name;
// 밑에 정보들을 저장하기 위해 만든것들을 임시로 보관해두는 용도.
// 자바는 한번에 클래스로 옮길 수 없다. 그래서 임시 보관함을 통한 다음 옮긴다.
// 아이디 입력 및 중복 체크
while (true) {
//올바른 아이디를 칠 때까지 물어본다.
System.out.print("로그인 아이디 : ");
loginId = sc.nextLine().trim();
//위에 상자를 넣어놔서 상자 이름만 말하면되서 문자열 표시하지 않음
boolean isJoinableId = true;
//무죄추정 원칙처럼 true라고 가정하고 밑 조건들을 본 다음 최종적으로 참 거짓 여부를 바꾼다.
//자바에서는 {} 안에서 태어난 변수는 {} 가 끝날 때 같이 죽으므로,
//검사를 다 마친 후 최종본을 알아야 하므로 조건문 전에 입력한다.
for (Member member : members) {
//회원 명부를 끝까지 다 뒤지는 타입.
//members : 뒤져볼 전체 회원 명부 리스트
// : 콜론은 오른쪽 명부에서 하나씩 꺼내서 왼쪽에 담으라는 기호
// Member memebr은 명부에서 꺼낸 회원 한명을 잠깐 쥐고 있을 변수이다.
//많은 데이터를 하나씩 뽑을 때 주로 사용한다.
if (member.getLoginId().equals(loginId)) {
//member.getLoginId()는 기존 장부에 있던 기존 회원 아이디이다.
//loginId 는 비교대상으로 새로 가입할는 아이디이다.
//. 의 뜻 : 손에 있는 리모컨(member)의 버튼(.)을 눌러서, 창고에 있는 진짜 본체의 아이디를 가져와(getLoginId)라는 뜻.
isJoinableId = false;
//두 개가 같으면 새로 가입할 의미가 없기에 거짓이라 한다.
//boolean isJoinableId = true; 위에 이 코드를 끄는것.
break;
// 현재 돌고 있는 반복문을 꺠고 밖으로 탈출해라.
}
}
if (isJoinableId == false) {
System.out.printf("%s(은)는 이미 사용중인 아이디입니다.\n", loginId);
//위에 출력문을 쓰지 않고 여기에 다시 쓴 이유 : 밑에 비밀번호를 입력이 있으므로
//만약 위에 출력문을 적었다면 출력만 하고 그대로 가입 절차를 진행시키는 버그가 생김.
//그래서
continue;
//밑으로 더 내려가지 말고 반복문 맨 위로 올라가서 처음부터 다시 시작해.
//방금 입력한 아이디는 이미 다른 사람이 쓰고 있어서 쓸 수 없으니, 새로운 아이디를 다시 생각해서 적으라고 기회를 주는 것.
//위 출력문을 여기에 적은 이유는 위에 출력문을 적었다면, 거짓 판명나서 break;로 나와서 바로 다음 비밀번호 단계로 내려가기에,
//다시 위로 올려보내기 위해 이 조건문을 적어 확인하고 continue로 다시 위로 돌려보낸다.
// 위에break 대신 continue를 쓰는 이유는 이 코드는 중복된 아이디 발견시 바로 종료하면 된다.
// 굳이 뒤에 id까지 검사할 필요가 없기에 이렇게 하는 것이 더 효율적이다.
}
break;
//id 단계 해결
//while문 종료.
}
// 비밀번호 입력 및 확인
while (true) {
System.out.print("로그인 비번 : ");
loginPw = sc.nextLine().trim();
System.out.print("로그인 비번 확인 : ");
String loginPwConfirm = sc.nextLine().trim();
if (loginPw.equals(loginPwConfirm) == false) {
System.out.println("비밀번호가 일치하지 않습니다. 다시 입력해주세요.");
continue;
}
break;
}
System.out.print("이름 : ");
name = sc.nextLine().trim();
int id = lastMemberId + 1;
//새 회원에게 고유 번호를 발급해 준다.
//컴퓨터 전용 불변의 번호가 필요하기 때문이다. -> 아디디 변경을 할 때 로그인 아이디로만 했다면
//그동안의 모든 것들을 다 뒤져서 해야하지만 컴퓨터 아이디로 하면, 겉으로 본이는 아이디나 이름이 바뀌어도
//컴퓨터는 아 이 번호의 회원이 바꾼거라고 인식하여서 헷갈리지 않음.
String regDate = Util.getNowStr();
//String regDate: 가입 날짜를 적어둘 빈칸을 준비하라는 뜻이다.
//Util: 다른 곳에 class Util이라고 도우미 클래스를 따로 만들어 두었다.)
//.getNowStr(): util 도구 상자 안에 있는 기능이다.
// 지금 당장(Now)의 시간을 글자(String)로 가져와(get)!"라는 뜻이다.
Member member = new Member(id, regDate, regDate, loginId, loginPw, name);
//메모리에 새로운 공간을 만들고, 거기에 데이터를 채워 넣는 과정이다.
//new Member(...): new 키워드는 메모리의 빈 공간을 확보하라는 직접적인 명령이다.
//확보한 공간에 Member 클래스에 적혀있는 모양대로 틀을 잡는다.
//그리고 괄호 안에 있는 6개의 변수(id, regDate...) 값들을 그 틀 안의 빈칸에 각각 대입한다.
//이때 this.id = id; 코드가 실행되는 것이다.
//regDate, regDate가 두 번 들어간 이유:
//Member 클래스의 생성자는 6개의 인자(인풋)를 받도록 설계되어 있다.
//순서대로 번호, 가입일, 수정일, 아이디, 비밀번호, 이름이다.
//지금은 최초 가입 시점이므로 '수정일'이 따로 존재할 수 없다.
//따라서 가입일과 동일한 데이터(regDate)를 두 번째와 세 번째 자리에 연속으로 넘겨주어 문법적 오류를 막고 데이터를 채운 것이다.
//Member member =:
//방금 메모리에 만들어진 데이터 덩어리(객체)를 코드에서 조종할 때 필요한 것.
//member라는 이름의 변수(참조 변수)를 만들어서, 방금 만들어진 메모리 공간의 주소(위치)를 저장해 두는 것이다.
//이제 member라는 이름만 부르면 그 데이터 덩어리에 접근할 수 있다.
//참조변수 : 거대한 물건(본체)을 직접 들고 있는 게 아니라,
// 그 물건을 조종할 수 있는 리모컨(주소)만 담아둔 주머니
members.add(member);
//방금 메모리에 생성한 새 회원 데이터(member)를,
//전체 회원을 관리하는 'members 리스트(배열)'의 맨 마지막 칸에 저장하는 명령어
System.out.printf("%d번 회원이 가입되었습니다.\n", id);
lastMemberId++;
// 가입 성공으로 발급한 번호를 공식적으로 1 올려놓으라는 명령어
}
// =======================================================
// [2] 게시글 작성 (article write)
// =======================================================
else if (cmd.equals("article write")) {
System.out.println("== 게시글 작성 ==");
int id = lastArticleId + 1;
System.out.print("제목 : ");
String title = sc.nextLine().trim();
System.out.print("내용 : ");
String body = sc.nextLine().trim();
// 맨 밑에 만든 Util 클래스(도구상자)를 이용해 현재 시간을 쉽게 가져옵니다.
// (★ 회원님의 날짜 구하는 자세한 주석은 맨 밑 Util 클래스 안으로 옮겼습니다!)
String regDate = Util.getNowStr();
String updateDate = Util.getNowStr(); // 처음엔 작성일과 수정일이 같습니다.
Article article = new Article(id, regDate, updateDate, title, body);
//설계도를 보고 진짜 실체를 만들어내는 과정이라고 해석한다.
//new Article(...): Article이라는 설계도를 보고,
//방금 위에서 입력받은 id, title, content 재료를 다 집어넣어서 새로운 진짜 게시글 1개를 만들어라 라는 뜻이다.
// Article article =: 그렇게 만들어진 진짜 게시글(우변)에 article이라는 이름표를 붙여서 내가 컨트롤하겠다는 뜻이다.
// (앞의 대문자 Article은 타입(설계도 종류)이고, 소문자 article은 제가 마음대로 지은 이름표이다.)
articles.add(article);
// articles는 맨 위에서 만든 게시글을 담아주는 리스트(상자)이다.
// .add( ) 는 리스트가 제공하는 기능으로, 상자 안에 무언가를 집어넣으라는 뜻이다.
// article 은 위 Article article = new Article(id, title, content); 여기서 이름표 붙인 진짜 게시글이다.
System.out.printf("%d 번 게시글이 작성되었습니다.\n", id);
// 형식을 저장해서 변수를 대입하려면 printf 를 사용해야 한다.
// printf 는 자동 줄바꿈을 하지 않으므로 \n 을 붙이는게 좋다.
lastArticleId++;
//게시글 전용 번호표 기계'의 숫자를 1칸 올려두는 마무리 작업
}
// =======================================================
// [3] 목록 보기 및 검색 (article list [검색어])
// =======================================================
else if (cmd.startsWith("article list")) {
String searchKeyword = cmd.substring("article list".length()).trim();
//cmd에 들어있는 전체 문장에서, 'article list'의 글자 수만큼 앞부분을 잘라내고,
//남은 글자의 양끝 공백을 없앤 최종 결과물을 searchKeyword라는 문자열 변수에 저장하라는 뜻.
//. (점) : 기능은 접근 연산자로 "앞에 있는 데이터가 가지고 있는 내장 기능(메서드)을 꺼내 쓰겠다"는 연결 고리이다.
//substring(...) 은 문자열 절단 함수로 괄호 안에 들어간 숫자(인덱스) 위치부터 맨 끝까지를 잘라서 가져오는 기능이다.
List<Article> forPrintArticles = articles;
//자바에서는 새로운 상자(변수)를 만들 때, "이 상자에 앞으로 뭘 담을 건지" 컴퓨터에게 무조건 미리 신고해야 하기 때문이다.
//List: 이 상자에는 데이터 하나가 아니라, 여러 개를 줄줄이 엮어서 담을 거다
//<Article>: "그리고 그 내용물은 '게시글'만 허용한다
if (searchKeyword.length() > 0) {
forPrintArticles = new ArrayList<>();
//forPrintArticles: 처음엔 큰 통을 같이 쓰다가, 플라스틱만 골라내라는 명령(if)이 떨어지면
// 새로운 빈 봉투(new ArrayList)를 꺼내는 겁니다. 그다음 큰 통에서 플라스틱을 발견할 때마다 그 새 봉투에만 담는 것이다.
for (Article article : articles) {
if (article.getTitle().contains(searchKeyword)) {
//.contains : 괄호 안에 넣은 글자가 앞에 있는 문장에 들어있는지 없는지만 확인한다.
// 결과로 (true) 아니면 (false)
forPrintArticles.add(article);
}
}
}
System.out.println("== 게시물 목록 ==");
if (forPrintArticles.isEmpty()) {
//isEmpty() : 리스트 안에 내용 유무를 체크해서 '참/거짓'으로 답해주는 자바 전용 탐지기이다.
//내용이 있다면 false 로 답하여서 if문을 벗어나 밑으로 내려간다.
//내용이 없다면 거짓으로 true로 답하고 continue를 다시 (명령어 : ) 로 올라간다.
System.out.println("시스템이 존재하지 않습니다.");
continue;
}
System.out.println(" 번호 / 날짜 / 제목 / 내용 ");
for (int i = forPrintArticles.size() - 1; i >= 0; i--) {
// 최신 글부터 역순으로 보여주기 위한 장치이다.
// 리스트를 0부터 센다.
// int i = articles.size() - 1 은 총 개수에서 제일 끝자리부터 시작한다는 뜻이다.
// -1은 저장공간이 3개이면 0부터 시작하여 2까지의 3칸이 마련되기에 최신것을 부르려면 -1을 해야 한다.
// 줄여가며 하나씩 간다.
//리스트에 있는 게시글을 뒤에서 부터 (최신순으로) 꺼낸다.
Article article = forPrintArticles.get(i);
//상자 리스트에서 i번째 게시글을 꺼내서 article 이라는 이름표를 붙여주는 과정이 출력문 바로 위에서 필요하다.
//get(i)는 그 상자에서 i번째 칸에 있는 게시물 하나를 손으로 끄집어내는 것이다.
//꺼낸 게시물에 article이라는 임시 이름표를 붙여준다.
//이렇게 해야 바로 밑줄에서 article.getTitle()처럼 그 게시물에게 말을 걸 수 있기 때문이다.
String today = Util.getNowStr().split(" ")[0];
// 오늘 날짜만.
// 손에 "2026-04-12 23:20:14" 라고 적힌 긴 종이 한 장이 있다.
// split(" ") 으로 띄어쓰기(" ")가 있는 곳을 가위로 싹둑 자른다. 이제 종이가 2장이 되었다.
// 앞장 : "2026-04-12" , 뒷장 : "23:20:14"
// [0] : 앞장 선택 . 컴퓨터는 숫자를 셀 때 1이 아니라 0부터 센다.
// 0번 종이 = 앞장 (2026-04-12), 1번 종이 = 뒷장 (23:20:14)
// 즉, [0]은 자른 종이 중에서 "0번째(앞장) 종이만 나한테 넘기라는 뜻이다.
// String today = : 서랍에 보관, 넘겨받은 0번째 종이(날짜)를 책상에 있는 today라는 이름표가 붙은 서랍에 집어넣으라는 뜻이다.
String regDay = article.getRegDate().split(" ")[0];
// 게시물 작성 날짜만.
if (today.equals(regDay)) {
System.out.printf(" %d / %s / %s / %s \n", article.getId(), article.getRegDate().split(" ")[1], article.getTitle(), article.getBody());
} else {
System.out.printf(" %d / %s / %s / %s \n", article.getId(), regDay, article.getTitle(), article.getBody());
}
//어떤 article인지 컴퓨터가 몰라서 위의 코드를 입력해줘야 한다.
}
}
// =======================================================
// [4] 상세 보기 (article detail)
// =======================================================
else if (cmd.startsWith("article detail ")) {
System.out.println("== 게시글 상세보기 ==");
// 1. "article detail 1" 이라는 문장을 띄어쓰기 기준으로 쪼개서, 3번째 조각("1")을 진짜 숫자로 바꿉니다.
String[] cmdBits = cmd.split(" ");
//잘린 조각들이 cmdBits라는 칸막이 상자(배열)에 순서대로 담긴다.
//[0]번 칸 : "article"
//[1]번 칸 : "delete"
//[2]번 칸 : "5" (우리가 필요한 글 번호)
int id = Integer.parseInt(cmdBits[2]);
//[2]번 칸 : "5" (우리가 필요한 글 번호)를 숫자로 변환.
Article foundArticle = null; // 찾은 게시글을 담을 '임시 빈 상자'
// 2. 리스트 상자를 처음부터 끝까지 샅샅이 뒤집니다.
for (Article article : articles) {
if (article.getId() == id) { // "너 번호가 내가 찾는 번호랑 똑같아?"
foundArticle = article; // 찾았다면 임시 상자에 담고
break; // 그만 찾고 나옵니다.
}
}
// 3. 다 뒤졌는데도 임시 상자가 비었다면 = 없는 번호
if (foundArticle == null) {
System.out.printf("%d번 게시글은 없습니다\n", id);
} else {
// 상자에 글이 있다면 예쁘게 양식에 맞춰 꺼내 보여줍니다.
System.out.println("번호 : " + foundArticle.getId());
System.out.println("작성날짜 : " + foundArticle.getRegDate());
System.out.println("수정날짜 : " + foundArticle.getUpdateDate());
System.out.println("제목 : " + foundArticle.getTitle());
System.out.println("내용 : " + foundArticle.getBody());
}
}
// =======================================================
// [5] 게시글 삭제 (article delete)
// =======================================================
else if (cmd.startsWith("article delete ")) {
System.out.println("== 게시글 삭제 ==");
//.equals() 글자 하나하나가 완전히 똑같아야 한다.
//.startsWith() 지정한 문자열로 시작만 하면 된다.
//이전 단계에서 사용했던 `article write`나 `article list`는 보통 그 자체로 명령이 끝나지만,
//삭제나 상세 보기는 "어떤 글을 삭제할 것인가?"에 대한 정보가 필요하다.
//사용자가 `article delete 1` 를 입력할 때,
//프로그램 입장에서는 `1`이라는 숫자가 매번 바뀔 텐데, `equals`를 쓰면 모든 경우의 수를 다 적어야 한다.
//그래서 앞부분이 `article delete `로 시작하는지만 확인하고 그 뒤에 붙는 숫자는 나중에 따로 쪼개서 읽자"라는 전략을 취하는 것이다.
//`equals`: "이거랑 토씨 하나 안 틀리고 똑같나?" (고정된 명령어)
//`startsWith`: "일단 시작은 이걸로 하나? 뒤에 추가적인게 있어도 괜찮다." (데이터가 포함된 명령어)
String[] cmdBits = cmd.split(" ");
int id = Integer.parseInt(cmdBits[2]);
//`split(" ")`의 특성은 문장을 띄어쓰기로 자르는 순간, 몇 조각이 나올지 이미 확정되는 것이다. (예: "article delete 1" → 무조건 3조각).
//결론은 조각의 개수가 변할 일이 없으므로, 굳이 무거운 `List`를 쓰기보다는 가볍고 빠른 배열을 사용하는 것이 컴퓨터 자원을 아끼는 방법이다.
//1. `cmd` (원본 데이터): 사용자가 입력한 전체 문장입니다. (예: `"article delete 1"`)
//2. `.split(" ")`:** 띄어쓰기(공백)를 기준으로 문장을 자른다.
//3. `String[] cmdBits`: 잘려진 조각들을 `cmdBits`라는 이름의 배열 상자에 순서대로 담는다.
//결과물: `cmdBits[0]` 에는 `"article"`, `cmdBits[1]` 에는 `"delete"`, `cmdBits[2]` 에는 `"1"` 이 들어갑니다.
//이렇게 쪼개 놓으면, 나중에 "무슨 명령을 내렸는지(`delete`)", "몇 번 글인지(`1`)" 필요할 때마다 번호(인덱스)표를 보고 쏙쏙 뽑아 쓰기 편해진다.
Article foundArticle = null;
//결론은 찾은 위치(게시물)를 저장할 건데, 아직은 못 찾았으니까 비어있다(null)고 해두자 라는 뜻입니다.
for (Article article : articles) {
//보관함(List)에 있는 게시물들을 처음부터 끝까지 하나씩 꺼내서 확인해보는 과정
if (article.getId() == id) {
// article.getId(): 방금 사물함에서 막 꺼내서 내 손에 쥔 그 게시물의 실제 번호이다.
// id: 사용자가 맨 처음에 입력했던 내가 찾고 있는 바로 그 번호이다.
foundArticle = article;
//찾았다는 표시를 남겨야 합니다
break;
}
}
if (foundArticle == null) {
System.out.printf("%d번 게시글은 존재하지 않습니다.\n", id);
} else {
articles.remove(foundArticle);
//제거하는 함수는 remove();이다.
System.out.printf("%d번 게시글이 삭제되었습니다.\n", id);
}
}
// =======================================================
// [6] 게시글 수정 (article modify)
// =======================================================
else if (cmd.startsWith("article modify ")) {
System.out.println("== 게시글 수정 ==");
// 1. 명령어 쪼개서 숫자(ID) 뽑아내기
String[] cmdBits = cmd.split(" ");
int id = Integer.parseInt(cmdBits[2]);
Article foundArticle = null;
// 찾은 게시글을 담을 빈 공간
// null = (초기 상태): 지금은 탐색을 시작하기 전이니까, 아무것도 없는 '빈손(null)' 상태이다.
// 숫자에서는 "없다"를 표현할 때 0이나 -1을 썼지만,
// 게시물(객체)처럼 덩치가 큰 데이터에서는 비었다는 것을 표현할 때 null 이라는 단어를 쓴다.
// 2. 리스트를 뒤져서 해당 ID를 가진 게시글 찾기
for (Article article : articles) {
if (article.getId() == id) {
foundArticle = article; // 찾았다면 빈 공간에 쏙!
break; // 찾았으니 반복문 탈출
}
}
// 3. 찾은 결과에 따라 다르게 처리하기
if (foundArticle == null) {
// 못 찾았을 때
System.out.printf("%d번 게시글은 없습니다.\n", id);
} else {
// 찾았을 때 (수정 진행)
// 3-1. 기존 내용 보여주기
System.out.println("기존 title : " + foundArticle.getTitle());
//자바에서 . (점, Dot)은 한국어로 "~의 안에 있는" 또는 **"~가 가지고 있는"**이라는 뜻이다.
//foundArticle: 조금 전 for문을 돌려서 찾아낸 다음, 쥐고 있는 게시글 전체(바구니)'
//foundArticle.getTitle()은 "내 손에 쥐고 있는 찾은 게시글(foundArticle)의 제목(title) 이라는 뜻이 된다.
System.out.println("기존 body : " + foundArticle.getBody());
// 3-2. 새로운 내용 입력받기
System.out.print("새 제목 : ");
String newTitle = sc.nextLine().trim();
System.out.print("새 내용 : ");
String newBody = sc.nextLine().trim();
// 3-3. 기존 게시글의 데이터 덮어쓰기 (★수정의 핵심★)
foundArticle.setTitle(newTitle);
foundArticle.setBody(newBody);
foundArticle.setUpdateDate(Util.getNowStr());
System.out.printf("%d번 게시글이 수정되었습니다.\n", id);
}
}
else {
System.out.println("사용할 수 없는 명령어 입니다.");
}
}
System.out.println("== 프로그램 종료 ==");
sc.close();
}
}
// ========================================================================
// 캡슐화가 적용된 Member(회원) 클래스
// ========================================================================
class Member {
// 신규 회원이 가입할 때, 그 사람의 정보를 담아둘 **'회원 전용 상자'**를 만드는 틀이다.
private int id;
private String regDate;
private String updateDate;
private String loginId;
private String loginPw;
private String name;
// 아이디, 비밀번호 같은 정보 앞에 private이 붙어 있다.
// 이건 외부에서 함부로 열어보고 조작하지 못하게 철통 보안(자물쇠)을 걸어라는 뜻이다. (이것을 '캡슐화'라고 부른다.)
public Member(int id, String regDate, String updateDate, String loginId, String loginPw, String name) {
//6개의 부품을 하나의 상자에 싹 쓸어 담고 16번 회원 홍길동 세트 완성하고 포장해 주는 역할이 바로 저 Member(...) 코드이다.
this.id = id;
this.regDate = regDate;
this.updateDate = updateDate;
this.loginId = loginId;
this.loginPw = loginPw;
this.name = name;
}
public int getId() { return id; }
public String getLoginId() { return loginId; }
public String getLoginPw() { return loginPw; }
public String getName() { return name; }
// getId(), getName() 등 (안내 데스크 - Getter): 상자를 자물쇠(private)로 잠가버려서,
// 나중에 회원 이름이 필요할 때 훔쳐볼 수가 없다.
// 그래서 "합법적으로 내용물을 물어볼 수 있는 정식 창구"를 뚫어놓은 것이다.
}
// ========================================================================
// 캡슐화가 적용된 Article(게시글) 클래스
// ========================================================================
class Article {
private int id;
private String regDate; // 실시간 날짜를 담아둘 공간
private String updateDate;
private String title;
private String body;
public Article(int id, String regDate, String updateDate, String title, String body) {
this.id = id;
//날짜를 담아줄 공간
this.regDate = regDate; // 매개변수로 들어온 실시간 날짜를 내 공간에 저장
this.updateDate = updateDate;
this.title = title;
this.body = body;
//public은 메인 클래스에서도 자유롭게 쓸 수 있도록 허락해 주는 키워드이다.
//생성자 Article( ) 은 객체가 만들어질 때 해야 할 초기 설정들을 해주느 공장 조립 라인이다.
//객체가 만들어 질 때 id, title, content등을 다 채워 만들어지게 한다. = 빈 껍데기가 아닌 내용 있게 함
// () 안에 있는 id와 클래스가 가진 (게시글에 저장될 공간) id의 이름이 동일하다.
// 따라서 this를 붙이면 이 설계도로 만든 나 자신의 것이라는 뜻이 된다.
// this.id = id;는 "내가 가진 id 공간(this.id)에,
// 방금 외부에서 입력받은 id 값(id)을 집어넣어라!"라는 명확한 지시어이다.
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getRegDate() { return regDate; }
public void setRegDate(String regDate) { this.regDate = regDate; }
public String getUpdateDate() { return updateDate; }
public void setUpdateDate(String updateDate) { this.updateDate = updateDate; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; }
//set는 반환하지 않아도 되므로 void를 쓰고 get은 반환해야 하므로 return 을 쓴다.
//괄호 안의 String body가 새 서류이다.
//이 새 서류를 들고 창고로 가서, 기존 내용을 파기하고 새것으로 교체(=)한다.
// 회원의 아이디나 고유번호는 한 번 가입하면 바뀔 일이 거의 없지만,
// 게시글은 나중에 '수정(modify)'을 엄청나게 많이 하기 때문에 setter을 사용함.
// 역할: 기존에 있던 제목을 버리고, 내가 새로 가져온 제목으로 내용물을 갈아 끼우라고
// 지시하는 **'수정 전용 합법 창구(Setter)입니다.
}
// ========================================================================
// 시간을 예쁘게 포장해주는 도구상자 (Util)
// ========================================================================
class Util {
public static String getNowStr() {
// 이 두 줄이 무조건 있어야 regDate(실시간 시간)가 탄생한다!
LocalDateTime now = LocalDateTime.now();
// LocalDateTime (로컬데이트타임): 자바가 가지고 있는 **'디지털 시계'**의 이름(타입)입니다.
// Date(날짜)와 Time(시간)을 같이 가지고 있다는 뜻이죠.
// now: 우리가 만든 변수(상자) 이름입니다. '지금'이라는 뜻으로 제 마음대로 지은 겁니다.
// LocalDateTime.now(): 자바의 시계에게 **"지금 당장(now) 시간을 딱 캡처해!"**라고 명령하는 기능입니다.
// 결과: 컴퓨터는 아주 복잡하고 정밀한 시간을 now라는 상자에 담아둡니다.
String regDate = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// String regDate =: 변환된 예쁜 글자(String)를 담을 regDate(등록일)라는 빈 상자를 만듭니다.
// now.format(...): 아까 위에서 시계 보고 캡처해 둔 now 시간에게
// "괄호 안에 있는 양식(format)대로 모습을 좀 바꾸라고 시키는것.
// DateTimeFormatter.ofPattern(...): 어떤 양식으로 바꿀지를 정해주는 도구이다.
// "yyyy-MM-dd HH:mm:ss": 대망의 양식이다. 알파벳마다 뜻이 다 정해져 있다.
return regDate;
}
}