사이먼's 코딩노트
[Java] 게시판 제작(8) 본문
[게시판 제작 ~ing]
- 작성된 모든 코드는 저의 깃허브 리포지터리에서 확인하실 수 있습니다.
- 리포지터리 주소 URL : https://github.com/psm817/full_stack_proj_2024_03
[클래스와 패키지 분리]
- 프로그램이 길고 복잡할 수록 모든 메서드와 변수 선언들을 Main 클래스에서 할 수는 없다.
- 모든 기능들을 Main 클래스에서 구현하게 되면 유지보수도 힘들뿐더러 가독성도 좋지 않다.
- 그래서 현재까지 구현된 게시물 관련 코드와 회원 관련 코드를 따로 분리시켜려 한다.
- 코드를 따로 이전 및 분리를 시키는 것이지, 코드 자체를 수정하는 것은 아니다.
package org.example.dto;
public class Article {
public int id;
public String regDate;
public String title;
public String body;
public int hit;
// 테스트 데이터는 조회수를 강제로 포함하고싶으면 Article 생성자를 오버로딩한다.
public Article(int id, String regDate, String title, String body, int hit) {
this.id = id;
this.regDate = regDate;
this.title = title;
this.body = body;
this.hit = hit;
}
// 복잡하게 쓰는거보다 this 메서드를 이용해서 간단하게 넘겨준다.
public Article(int id, String regDate, String title, String body) {
this(id, regDate, title, body, 0);
}
// 메서드에 privite를 붙히면 외부 클래스에서 사용할 수 없다.
// 메서드에 public를 붙히면 외내부 전부 사용할 수 있다.
public void increaseHit() {
hit++;
}
}
- 가장 먼저 해줄 부분은 각 게시물 생성 시 필요한 데이터 정보들을 Article.java라는 새로운 클래스를 생성하여 이전시켜준다.
- Article.java는 dto라는 패키지를 만들어 해당 패키지 안으로 클래스를 이전시켜준다.
- 여기서 말하는 dto는 데이터를 관리하는 곳으로 생각하면 좋고 앞으로 게시물이나 회원에 관련된 기본 정보 데이터들의 관리는 모두 dto 패키지에 모아둘 예정이다.
package org.example.dto;
// public을 붙혀야 Main에서 불러왔을 때 오류가 안난다.
public class Member {
public int id;
public String regDate;
public String loginId;
public String loginPw;
public String name;
// 테스트 데이터는 조회수를 강제로 포함하고싶으면 Article 생성자를 오버로딩한다.
public Member(int id, String regDate, String loginId, String loginPw, String name) {
this.id = id;
this.regDate = regDate;
this.loginId = loginId;
this.loginPw = loginPw;
this.name = name;
}
}
- 그 다음은 각 회원 생성 시 필요한 데이터 정보들을 Member.java라는 새로운 클래스를 생성하여 이전시켜준다.
- 마찬가지로 회원의 관련된 기본 정보 데이터들이기 때문에 dto 패키지 안에 클래스를 이전시켜준다.
[회원 관련 기능 Controller로 이전]
- 위에서 Article과 Member를 생성하는 부분을 클래스로 따로 두어 분리해줬다면 이번에는 각 게시물과 회원이 하는 기능들을 따로 분리해준다.
- 먼저 회원 관련 기능부터 Controller를 도입하여 이전시키려한다.
- 여기서 말하는 회원 관련 기능이란 member join이라는 명령어를 실행했을 때 동작하는 로직들을 의미한다.
package org.example.controller;
import org.example.dto.Member;
import org.example.util.Util;
import java.util.List;
import java.util.Scanner;
public class MemberController {
private Scanner sc;
private List<Member> members;
public MemberController(Scanner sc, List<Member> members) {
this.sc = sc;
this.members = members;
}
public void doJoin() {
int id = members.size() + 1;
String regDate = Util.getNowDateStr();
String loginId = null;
String loginPw = null;
String loginPwConfirm = null;
while(true) {
System.out.printf("로그인 아이디 : ");
loginId = sc.nextLine();
if(isJoinableLoginId(loginId) == false) {
System.out.printf("%s(은)는 이미 사용중인 아이디 입니다.\n", loginId);
continue;
}
break;
}
while(true) {
System.out.printf("로그인 비번 : ");
loginPw = sc.nextLine();
System.out.printf("로그인 비번확인 : ");
loginPwConfirm = sc.nextLine();
if(loginPw.equals(loginPwConfirm) == false) {
System.out.println("비밀번호를 다시 입력해주세요.");
continue;
}
break;
}
System.out.printf("이름 : ");
String name = sc.nextLine();
Member member = new Member(id, regDate, loginId, loginPw, name);
members.add(member);
System.out.printf("%d번 회원이 생성되었습니다.\n", id);
}
private boolean isJoinableLoginId(String loginId) {
int index = getMemberIndexByLoginId(loginId);
if(index == -1) {
return true;
}
return false;
}
private int getMemberIndexByLoginId(String loginId) {
int i = 0;
for(Member member : members) {
if(member.loginId.equals(loginId)) {
return i;
}
i++;
}
return -1;
}
}
- MemberController.java 클래스가 하는 역할은 Main 클래스에서 동작했던 members라는 리스트를 생성하고 회원을 등록할 때마다 해당 리스트에 회원 정보를 저장한다.
- 추후에 회원 관련된 기능이 추가된다면 MemberController.java 클래스에 기능을 추가하면된다.
- 코드가 크게 바뀐 것은 없지만 Main 클래스에서 컨트롤러를 통해 MemberController.doJoin()이라는 메서드를 호출했을 때 값을 넘겨줄 수 있도록 메서드 명을 doJoin()으로 선언하였다.
[핵심 로직을 App으로 이전, 상속해줄 수 있는 dto 생성]
[핵심로직 App으로 이전]
package org.example;
import org.example.controller.ArticleController;
import org.example.controller.MemberController;
import org.example.dto.Article;
import org.example.dto.Member;
import org.example.util.Util;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class App {
private List<Article> articles;
private List<Member> members;
public App() {
articles = new ArrayList<>();
members = new ArrayList<>();
}
public void start() {
System.out.println("== 프로그램 시작 ==");
makeTestData();
Scanner sc = new Scanner(System.in);
MemberController memberController = new MemberController(sc, members);
ArticleController articleController = new ArticleController();
while(true) {
System.out.printf("명령어) ");
String cmd = sc.nextLine();
cmd = cmd.trim();
if(cmd.length() == 0) {
continue;
}
if(cmd.equals("system exit")) {
break;
}
else if(cmd.equals("member join")) {
memberController.doJoin();
}
else if(cmd.equals("article write")) {
int id = articles.size() + 1;
System.out.printf("제목 : ");
String title = sc.nextLine();
System.out.printf("내용 : ");
String body = sc.nextLine();
String regDate = Util.getNowDateStr();
Article article = new Article(id, regDate, title, body);
articles.add(article);
System.out.printf("%d번 글이 작성되었습니다.\n", id);
}
else if(cmd.startsWith("article list")) {
if(articles.size() == 0) {
System.out.println("게시물이 없습니다.");
continue;
}
String searchKeyword = cmd.substring("article list".length()).trim();
List<Article> forListArticles = articles;
if(searchKeyword.length() > 0) {
forListArticles = new ArrayList<>();
for(Article article : articles) {
if(article.title.contains(searchKeyword)) {
forListArticles.add(article);
}
}
}
if (forListArticles.size() == 0) {
System.out.println("검색결과가 존재하지 않습니다.");
continue;
}
System.out.println("번호 | 조회수 | 제목");
for(int i = forListArticles.size() - 1; i >= 0; i--) {
Article article = forListArticles.get(i);
System.out.printf("%4d | %4d | %s\n", article.id, article.hit, article.title);
}
}
// ------- 상세보기 -------
else if(cmd.startsWith("article detail ")) {
String[] cmdBits = cmd.split(" ");
int id = Integer.parseInt(cmdBits[2]);
Article foundArticle = getArticleById(id);
if(foundArticle == null) {
System.out.printf("%d번 게시물은 존재하지 않습니다.\n", id);
continue;
}
// 조회수 늘리기
foundArticle.increaseHit();
System.out.printf("번호 : %d\n", foundArticle.id);
System.out.printf("최초등록날짜 : %s\n", foundArticle.regDate);
System.out.printf("제목 : %s\n", foundArticle.title);
System.out.printf("내용 : %s\n", foundArticle.body);
System.out.printf("조회수 : %d\n", foundArticle.hit);
}
// ------- 수정 -------
else if(cmd.startsWith("article modify ")) {
String[] cmdBits = cmd.split(" ");
int id = Integer.parseInt(cmdBits[2]);
Article foundArticle = getArticleById(id);
if(foundArticle == null) {
System.out.printf("%d번 게시물은 존재하지 않습니다.\n", id);
continue;
}
System.out.printf("제목 : ");
String title = sc.nextLine();
System.out.printf("내용 : ");
String body = sc.nextLine();
foundArticle.title = title;
foundArticle.body = body;
System.out.printf("%d번 게시물이 수정되었습니다.\n", id);
}
// ------- 삭제 -------
else if(cmd.startsWith("article delete ")) {
String[] cmdBits = cmd.split(" ");
int id = Integer.parseInt(cmdBits[2]);
int foundIndex = getArticleIndexById(id);
if(foundIndex == -1) {
System.out.printf("%d번 게시물은 존재하지 않습니다.\n", id);
continue;
}
articles.remove(foundIndex);
System.out.printf("%d번 게시물이 삭제되었습니다.\n", id);
}
else {
System.out.printf("%s(은)는 존재하지 않는 명령어입니다.\n", cmd);
}
}
sc.close();
System.out.println("== 프로그램 끝 ==");
}
private int getArticleIndexById(int id) {
int i = 0;
for(Article article : articles) {
if(article.id == id) {
return i;
}
i++;
}
return -1;
}
// 공통된 기능을 하나의 메서드로 집합
private Article getArticleById(int id) {
int index = getArticleIndexById(id);
if(index != -1) {
return articles.get(index);
}
return null;
}
private void makeTestData() {
System.out.println("테스트를 위한 데이터를 생성합니다.");
articles.add(new Article(1, Util.getNowDateStr(), "제목1", "내용1", 10));
articles.add(new Article(2, Util.getNowDateStr(), "제목2", "내용2", 32));
articles.add(new Article(3, Util.getNowDateStr(), "제목3", "내용3", 103));
}
}
package org.example;
public class Main {
public static void main(String[] args) {
new App().start();
}
}
- 핵심 로직을 App으로 이전한다는 말은 분리하고 이전된 코드를 제외한 Main.java 클래스에 적힌 모든 코드들을 App.java라는 클래스로 이전하고 Main.java 클래스의 크기를 최소화 시키겠다는 뜻이다.
- App.java 클래스로 모두 이전시킬 때 실제로 Main.java 클래스에서 App을 호출하여 프로그램을 실행해야되기 때문에 start() 라는 메서드 아래로 이전시켜주면 편하다.
- 그렇게 되면 Main.java 클래스에서는 단순하게 new App().start()라는 코드 한줄로 프로그램을 실행할 수 있다.
[dto 생성]
package org.example.dto;
// article과 member를 묶어주는 상위클래스라고 생각
public class Dto {
public int id;
public String regDate;
}
public class Article extends Dto
public class Member extends Dto
- Article.java와 Member.java 클래스를 서로 분리했지만 다시 생각해봤을 때 게시물이든 회원이든 생성할 때 이름과 날짜는 모두 가지고 태어난다.
- 이럴 때는 최대한 중복을 막기 위해서 동시에 필요한 변수인 name과 regDate는 Dto.java라는 새로운 클래스를 생성하여 변수를 선언하고 Article.java와 Member.java 클래스에서 Dto를 상속받도록 해야한다.
반응형
'프로젝트 > [Java] 게시판 제작' 카테고리의 다른 글
[Java] 게시판 제작(10) (0) | 2024.03.07 |
---|---|
[Java] 게시판 제작(9) (0) | 2024.03.04 |
[Java] 게시판 제작(7) (0) | 2024.02.26 |
[Java] 게시판 제작(6) (0) | 2024.02.23 |
[Java] 게시판 제작(5) (0) | 2024.02.22 |