사이먼's 코딩노트

[Java] 게시판 제작(8) 본문

프로젝트/[Java] 게시판 제작

[Java] 게시판 제작(8)

simonpark817 2024. 3. 4. 22:05

[게시판 제작 ~ing]

 

GitHub - psm817/full_stack_proj_2024_03

Contribute to psm817/full_stack_proj_2024_03 development by creating an account on GitHub.

github.com

 

[클래스와 패키지 분리]

  • 프로그램이 길고 복잡할 수록 모든 메서드와 변수 선언들을 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