mini-pms 42

12 minute read

42. 비즈니스 로직 분리하기 : 서비스 객체의 도입

1단계 - 프로젝트 삭제 Command 객체에서 업무 코드를 분리한다.

커맨드 객체에서 비즈니스 로직을 분리하여 서비스 객체에 옮긴다.

  • com.eomcs.pms.service.ProjectService 인터페이스 생성
    • 서비스 객체의 메서드 명은 보통 업무 관련 용어를 사용한다.
    • DAO 객체의 메서드 명은 데이터 관련 용어를 사용한다.
    • delete() 메서드 선언
public interface ProjectService {
  int delete(int projectNo) throws Exception;

}
  • com.eomcs.pms.service.DefaultProjectService 클래스 생성
    • delete() 메서드 구현
      • ProjectDeleteCommand 에서 비즈니스 로직과 관련된 코드를 가져온다.
public class DefaultProjectService implements ProjectService {

  ProjectDao projectDao;
  TaskDao taskDao;
  SqlSessionFactoryProxy factoryProxy;

  public DefaultProjectService(
      ProjectDao projectDao,
      TaskDao taskDao,
      SqlSessionFactoryProxy factoryProxy) {
    this.projectDao = projectDao;
    this.taskDao = taskDao;
    this.factoryProxy = factoryProxy;
  }


  @Override
  public int delete(int no) throws Exception {
    try {
      // 트랜잭션 시작
      factoryProxy.startTransaction();
      // 프로젝트에 소속된 모든 작업 삭제하기
      taskDao.deleteByProjectNo(no);
      // 프로젝트 삭제하기
      int count = projectDao.delete(no);
      // 트랜잭션 작업이 성공하였다면 커밋
      factoryProxy.commit();
      return count;

    } catch (Exception e) {
      // 트랜잭션 작업 도중에 예외가 발생하면 롤백
      factoryProxy.rollback();
      // 서비스 객체에서 발생한 예외는 호출자에게 전달한다.
      throw e;

    } finally {
      // 트랜잭션 종료
      factoryProxy.endTransaction();
    }
  }
}
  • com.eomcs.pms.handler.ProjectDeleteCommand 클래스 변경
    • ProjectService 구현체를 사용하여 프로젝트 삭제 처리
public class ProjectDeleteCommand implements Command {

  ProjectService projectService;

  public ProjectDeleteCommand(ProjectService projectService) {
    this.projectService = projectService;
  }

  @Override
  public void execute(Map<String,Object> context) {
    System.out.println("[프로젝트 삭제]");
    int no = Prompt.inputInt("번호? ");

    String response = Prompt.inputString("정말 삭제하시겠습니까?(y/N) ");
    if (!response.equalsIgnoreCase("y")) {
      System.out.println("프로젝트 삭제를 취소하였습니다.");
      return;
    }

    try {

      if (projectService.delete(no) == 0) {
        System.out.println("해당 번호의 프로젝트가 존재하지 않습니다.");
        return;
      }
      System.out.println("프로젝트를 삭제하였습니다.");

    } catch (Exception e) {
      System.out.println("프로젝트 삭제 중 오류 발생!");
      e.printStackTrace();
    }
  }
}

2단계 - 프로젝트 삭제 DAO 객체에서 업무 코드를 분리한다.

DAO 객체에서 비즈니스 로직을 분리하여 서비스 객체에 옮긴다.

  • com.eomcs.pms.dao.ProjectDao 인터페이스 변경
    • deleteMembers() 메서드 선언 추가
public interface ProjectDao {
  int insert(Project project) throws Exception;
  int delete(int no) throws Exception;
  Project findByNo(int no) throws Exception;
  List<Project> findAll() throws Exception;
  List<Project> findByKeyword(String item, String keyword) throws Exception;
  List<Project> findByDetailKeyword(Map<String,Object> keywords) throws Exception;
  int update(Project project) throws Exception;

  int deleteMembers(int PorjectNo) throws Exception;
}
  • com.eomcs.pms.dao.mariadb.ProjectDaoImpl 클래스 변경
    • delete() 메서드에서 멤버 삭제 관련 코드를 별도의 메서드 deleteMembers() 로 분리한다.
public class ProjectDaoImpl implements com.eomcs.pms.dao.ProjectDao {
.
.
.
  @Override
  public int delete(int no) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      // 프로젝트를 삭제한다.
      return sqlSession.delete("ProjectDao.delete", no);
    }
  }
.
.
.
  @Override
  public int deleteMembers(int projectNo) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      // 프로젝트에 소속된 모든 멤버를 삭제한다.
      return sqlSession.delete("ProjectDao.deleteMembers", projectNo);
    }
  }
}
  • com.eomcs.pms.service.DefaultProjectService 클래스 변경
    • delete() 메서드 변경
      • 프로젝트 멤버를 삭제하는 deleteMembers() 를 호출한다.
public class DefaultProjectService implements ProjectService {
.
.
.
  @Override
  public int delete(int no) throws Exception {
    try {
      // 트랜잭션 시작
      factoryProxy.startTransaction();
      // 프로젝트에 소속된 모든 작업 삭제하기
      taskDao.deleteByProjectNo(no);
      // 프로젝트에 소속된 모든 멤버 삭제하기
      projectDao.deleteMembers(no);
      // 프로젝트 삭제하기
      int count = projectDao.delete(no);
      // 트랜잭션 작업이 성공하였다면 커밋
      factoryProxy.commit();
      return count;

    } catch (Exception e) {
      // 트랜잭션 작업 도중에 예외가 발생하면 롤백
      factoryProxy.rollback();
      // 서비스 객체에서 발생한 예외는 호출자에게 전달한다.
      throw e;

    } finally {
      // 트랜잭션 종료
      factoryProxy.endTransaction();
    }
  }
}

3단계 - 프로젝트 등록 커맨드 객체에서 비즈니스 로직을 분리한다.

  • com.eomcs.pms.dao.ProjectDao 인터페이스 변경
    • insertMembers() 메서드 선언 추가
public interface ProjectDao {
  int insert(Project project) throws Exception;
  int delete(int no) throws Exception;
  Project findByNo(int no) throws Exception;
  List<Project> findAll() throws Exception;
  List<Project> findByKeyword(String item, String keyword) throws Exception;
  List<Project> findByDetailKeyword(Map<String,Object> keywords) throws Exception;
  int update(Project project) throws Exception;

  int deleteMembers(int PorjectNo) throws Exception;
  int insertMembers(Project project) throws Exception;
}
  • com.eomcs.pms.dao.mariadb.ProjectDaoImpl 클래스 변경
    • insert() 메서드에서 비즈니스 로직을 추출하여 별도의 메서드 insertMembers() 로 옮긴다.
public class ProjectDaoImpl implements com.eomcs.pms.dao.ProjectDao {

  SqlSessionFactory sqlSessionFactory;

  public ProjectDaoImpl(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
  }

  @Override
  public int insert(Project project) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      // 프로젝트 정보 입력
      return sqlSession.insert("ProjectDao.insert", project);
    }
  }
.
.
.
  @Override
  public int insertMembers(Project project) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      // 프로젝트의 멤버 정보 입력
      return sqlSession.insert("ProjectDao.insertMembers", project);
    }
  }
}
  • com.eomcs.pms.service.ProjectService 인터페이스 변경
    • add() 메서드 선언
public interface ProjectService {
  int delete(int projectNo) throws Exception;
  int add(Project project) throws Exception;
}
  • com.eomcs.pms.service.DefaultProjectService 클래스 변경
    • add() 메서드 구현
      • ProjectAddCommand 에서 비즈니스 로직과 관련된 코드를 가져온다.
public class DefaultProjectService implements ProjectService {
.
.
.
  @Override
  public int add(Project project) throws Exception {
    projectDao.insert(project);
    return 1;
  }
}
  • project를 insert할 때 memberDao를 사용하기 때문에
    • com.eomcs.pms.service.MemberService 인터페이스 생성
      • list() 메서드 선언
public interface MemberService {
    // findByName 이기 때문에 이름을 리스트로 리턴한다.
  List<Member> list(String name) throws Exception;
}
  • com.eomcs.pms.service.DefaultMemberService 인터페이스 생성
    • list() 메서드 구현
public interface MemberDao {
  int insert(Member member) throws Exception;
  int delete(int no) throws Exception;
  Member findByNo(int no) throws Exception;
  List<Member> findByName(String name) throws Exception;
  List<Member> findAll() throws Exception;
  int update(Member member) throws Exception;
  List<Member> findByProjectNo(int projectNo) throws Exception;
  Member findByEmailPassword(String email, String password) throws Exception;
}

public class DefaultMemberService implements MemberService {

  MemberDao memberDao;

  public DefaultMemberService(MemberDao memberDao) {
    this.memberDao = memberDao;
  }

  @Override
  public List<Member> list(String name) throws Exception {
    return memberDao.findByName(name);
  }
}
  • com.eomcs.pms.service.MemberDao 인터페이스 변경
    • findByName() 의 리턴 값을 List 객체로 변경한다.
public interface MemberDao {
  int insert(Member member) throws Exception;
  int delete(int no) throws Exception;
  Member findByNo(int no) throws Exception;
  List<Member> findByName(String name) throws Exception;
  List<Member> findAll() throws Exception;
  int update(Member member) throws Exception;
  List<Member> findByProjectNo(int projectNo) throws Exception;
  Member findByEmailPassword(String email, String password) throws Exception;
}
  • com.eomcs.pms.service.MemberDaoImpl 클래스 변경
    • findByName() 의 리턴 값을 List 객체로 변경한다.
public class MemberDaoImpl implements com.eomcs.pms.dao.MemberDao {
.
.
.
	@Override
  public List<Member> findByName(String name) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      return sqlSession.selectList("MemberDao.findByName", name);
    }
  }
.
.
.
}
  • com.eomcs.pms.handler.ProjectAddCommand 클래스 변경
    • MemberService 구현체를 사용하여 멤버 찾기
    • ProjectService 구현체를 사용하여 프로젝트 등록 처리
public class ProjectAddCommand implements Command {

  ProjectService projectService;
  MemberService memberService;

  public ProjectAddCommand(ProjectService projectService, MemberService memberService) {
    this.projectService = projectService;
    this.memberService = memberService;
  }

  @Override
  public void execute(Map<String,Object> context) {
    System.out.println("[프로젝트 등록]");

    try {
      Project project = new Project();
      project.setTitle(Prompt.inputString("프로젝트명? "));
      project.setContent(Prompt.inputString("내용? "));
      project.setStartDate(Prompt.inputDate("시작일? "));
      project.setEndDate(Prompt.inputDate("종료일? "));

      Member loginUser = (Member) context.get("loginUser");
      project.setOwner(loginUser);

      // 프로젝트에 참여할 회원 정보를 담는다.
      List<Member> members = new ArrayList<>();
      while (true) {
        String name = Prompt.inputString("팀원?(완료: 빈 문자열) ");
        if (name.length() == 0) {
          break;
        } else {
          // Dao가 아니라 Service를 사용하게 한다.
          List<Member> list = memberService.list(name);
          if (list == null) {
            System.out.println("등록된 회원이 아닙니다.");
            continue;
          }
          // 리스트의 0번째에 담는다.
          members.add(list.get(0));
        }
      }

      // 사용자로부터 입력 받은 멤버 정보를 프로젝트에 저장한다.
      project.setMembers(members);

      // Dao가 아니라 Service를 사용하게 한다.
      projectService.add(project);
      System.out.println("프로젝트가 등록되었습니다!");

    } catch (Exception e) {
      System.out.println("프로젝트 등록 중 오류 발생!");
      e.printStackTrace();
    }
  }
}

4단계 - 프로젝트 목록 조회 커맨드에서 비즈니스 로직을 분리한다.

  • com.eomcs.pms.service.ProjectService 인터페이스 변경
    • list() 메서드 선언
public interface ProjectService {
  int delete(int projectNo) throws Exception;
  int add(Project project) throws Exception;
  List<Project> list(String keyword) throws Exception;
}
  • com.eomcs.pms.service.DefaultProjectService 클래스 변경
    • list() 메서드 구현
    • ProjectListCommand 에서 비즈니스 로직과 관련된 코드를 가져온다.
  public class DefaultProjectService implements ProjectService {
  .
  .
  .
  @Override
  public List<Project> list(String keyword) throws Exception {
    // 이 메서드를 보면 서비스 객체가 할 일이 없다.
    // 그냥 Dao 객체의 메서드를 호출한 다음에 리턴값을
    // 그대로 리턴해주는 일을 한다.
    // 그럼 왜 이런 메서드를 만들어야 하는가?
    // => 프로그래밍의 일관성을 위해서다
    // => 커맨드 객체가 상황에 따라 Service 객체를 쓰거나 Dao 객체를 써야 한다면
    //    프로그래밍의 일관성이 없어서 유지보수 하기가 어렵다.
    // => 커맨드 객체가 서비스 객체를 사용하기로 했으면
    //    어떤 작업을 수행하든간에 일관되게 서비스 객체를 사용하는 것이
    //    유지보수하기에 더 낫다.
    // => 그래서 이런 메서드를 만드는 것이다.
    //    즉, 서비스 객체의 메서드에서 특별히 할 일이 없다 하더라도
    //    커맨드 객체가 일관성 있게 작업을 수행할 수 있도록
    //    중간에서 Dao 객체의 메서드를 호출해주는 것이다.
    return projectDao.findAll(keyword);
  }
}
  • com.eomcs.pms.handler.ProjectListCommand 클래스 변경
    • ProjectService 구현체를 사용하여 프로젝트 목록 조회 처리
public class ProjectListCommand implements Command {
  ProjectService projectService;

  public ProjectListCommand(ProjectService projectService) {
    this.projectService = projectService;
  }

  @Override
  public void execute(Map<String,Object> context) {
    System.out.println("[프로젝트 목록]");

    try {
      // 오류가 나지 않도록 일단 null을 넣어둔다.
      List<Project> list = projectService.list((String) null);
      System.out.println("번호, 프로젝트명, 시작일 ~ 종료일, 관리자, 팀원");

      for (Project project : list) {
        StringBuilder members = new StringBuilder();
        for (Member member : project.getMembers()) {
          if (members.length() > 0) {
            members.append(",");
          }
          members.append(member.getName());
        }

        System.out.printf("%d, %s, %s ~ %s, %s, [%s]\n",
            project.getNo(),
            project.getTitle(),
            project.getStartDate(),
            project.getEndDate(),
            project.getOwner().getName(),
            members.toString());
      }
    } catch (Exception e) {
      System.out.println("프로젝트 목록 조회 중 오류 발생!");
      e.printStackTrace();
    }
  }
}

5단계 - 프로젝트 검색 커맨드에서 비즈니스 로직을 분리한다.

  • com.eomcs.pms.service.ProjectService 인터페이스 변경
    • list(String) 메서드 변경
      • 검색어를 받는 파라미터를 추가한다.
public interface ProjectService {
  int delete(int projectNo) throws Exception;
  int add(Project project) throws Exception;
  List<Project> list(String keyword) throws Exception;
}
  • com.eomcs.pms.service.DefaultProjectService 클래스 변경
    • list(String) 메서드 구현
      • 검색어를 받는 파라미터를 추가한다.
public class DefaultProjectService implements ProjectService {
.
.
.
	@Override
  public List<Project> list(String keyword) throws Exception {
    return projectDao.findByDetailKeyword(keyword);
  }
}
  • com.eomcs.pms.dao.ProjectDao 인터페이스 변경
    • findAll(String) 메서드를 변경한다.
    • findByKeyword() 메서드를 삭제한다.
      • findAll() 메서드와 합친다.
public interface ProjectDao {
  int insert(Project project) throws Exception;
  int delete(int no) throws Exception;
  Project findByNo(int no) throws Exception;
  List<Project> findAll(String keyword) throws Exception;
  List<Project> findByDetailKeyword(Map<String,Object> keywords) throws Exception;
  int update(Project project) throws Exception;

  int deleteMembers(int PorjectNo) throws Exception;
  int insertMembers(Project project) throws Exception;
}
  • com.eomcs.pms.dao.mariadb.ProjectDaoImpl 클래스 변경
    • 인터페이스 변경에 맞춰 findAll(String) 메서드를 변경한다.
    • findByKeyword() 메서드를 삭제한다.
      • 구현 기능을 findAll() 메서드에 합친다.
public class ProjectDaoImpl implements com.eomcs.pms.dao.ProjectDao {

  SqlSessionFactory sqlSessionFactory;

  public ProjectDaoImpl(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
  }
.
.
.
	@Override
  public List<Project> findAll(String keyword) throws Exception {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      return sqlSession.selectList("ProjectDao.findAll", keyword);
    }
  }
.
.
.
}
  • src/main/resources/com/eomcs/pms/mapper/ProjectMapper.xml 변경
    • findAll SQL 문을 변경한다.
    • findByKeyword SQL 문을 삭제한다.
      • findAll SQL 문과 기능을 합친다.
 <sql id="sql1">
    select 
      p.no, 
      p.title, 
      p.sdt, 
      p.edt, 
      m.no owner_no, 
      m.name owner_name,
      mp.member_no,
      m2.name member_name
    from 
      pms_project p 
      inner join pms_member m on p.owner=m.no
      left outer join pms_member_project mp on p.no=mp.project_no
      left outer join pms_member m2 on mp.member_no=m2.no
 </sql>  

<select id="findAll" resultMap="ProjectMap" parameterType="string">
    <!-- 별도로 분리된 SQL 코드를 가져오고 싶다면, 다음과 같이 하라. -->
    <include refid="sql1"/>
    <if test="keyword != null">
      where
        p.title like concat('%', #{keyword}, '%')
        or m.name like concat('%', #{keyword}, '%')
        or m2.name like concat('%', #{keyword}, '%')
    </if>
    order by p.no desc
 </select>
  • com.eomcs.pms.handler.ProjectListCommand 클래스 변경
    • ProjectService.list(String) 메서드 호출 코드를 변경한다.
public class ProjectListCommand implements Command {
  ProjectService projectService;

  public ProjectListCommand(ProjectService projectService) {
    this.projectService = projectService;
  }

  @Override
  public void execute(Map<String,Object> context) {
    System.out.println("[프로젝트 목록]");

    try {
      // 오류가 나지 않도록 일단 null을 넣어둔다.
      List<Project> list = projectService.list((String) null);
      System.out.println("번호, 프로젝트명, 시작일 ~ 종료일, 관리자, 팀원");
.
.
.
}
  • com.eomcs.pms.handler.ProjectSearchCommand 클래스 변경
    • ProjectService.list(String) 메서드를 사용하여 프로젝트를 검색한다.
public class ProjectSearchCommand implements Command {
  ProjectService projectService;

  public ProjectSearchCommand(ProjectService projectService) {
    this.projectService = projectService;
  }

  @Override
  public void execute(Map<String,Object> context) {
    System.out.println("[프로젝트 검색]");

    try {
      String keyword = Prompt.inputString("검색어? ");

      List<Project> list = projectService.list(keyword);
      System.out.println("번호, 프로젝트명, 시작일 ~ 종료일, 관리자, 팀원");
.
.
.
}

6단계 - 프로젝트 상세 검색 커맨드에서 비즈니스 로직을 분리한다.

  • com.eomcs.pms.service.ProjectService 인터페이스 변경
    • list(Map<String,Object> keywords) 메서드 추가(오버로딩)
      • 검색 항목과 검색어를 입력 받는 파라미터를 추가한다.
public interface ProjectService {
  int delete(int projectNo) throws Exception;
  int add(Project project) throws Exception;
  List<Project> list(String keyword) throws Exception;

  // ProjectListCommand가 Map 객체를 받는다?
  // List 오버로딩한다.
  //메서드의 파라미터 개수나 파라미터 타입이 다르더라도
  // 같은 기능을 수행한다면
  // 같은 이름을 부여함으로서 프로그래밍의 일관성을 유지하게 도와주는 문법
  List<Project> list(Map<String,Object> keywords) throws Exception;
}
  • com.eomcs.pms.service.DefaultProjectService 클래스 변경
    • list(Map<String,Object> keywords) 메서드 구현(오버로딩)
      • 검색 항목과 검색어를 입력 받는 파라미터를 추가한다.
public class DefaultProjectService implements ProjectService {
.
.
.
	@Override
  public List<Project> list(Map<String, Object> keywords) throws Exception {
    return projectDao.findByDetailKeyword(keywords);
  }
}
  • com.eomcs.pms.handler.ProjectDetailSearchCommand 클래스 변경
    • ProjectService.list(Map) 메서드를 사용하여 프로젝트를 검색한다.
public class ProjectDetailSearchCommand implements Command {
  ProjectService projectService;

  public ProjectDetailSearchCommand(ProjectService projectService) {
    this.projectService = projectService;
  }
  @Override
  public void execute(Map<String,Object> context) {
    System.out.println("[프로젝트 상세 검색]");

    try {
      HashMap<String,Object> keywords = new HashMap<>();

      String title = Prompt.inputString("프로젝트명? ");
      if (title.length() > 0) {
        keywords.put("title", title);
      }

      String owner = Prompt.inputString("관리자명? ");
      if (owner.length() > 0) {
        keywords.put("owner", owner);
      }

      String member = Prompt.inputString("팀원명? ");
      if (member.length() > 0) {
        keywords.put("member", member);
      }

      List<Project> list = projectService.list(keywords);
      System.out.println("번호, 프로젝트명, 시작일 ~ 종료일, 관리자, 팀원");
.
.
.
}

7단계 - 프로젝트 상세 조회 커맨드에서 비즈니스 로직을 분리한다.

  • com.eomcs.pms.service.ProjectService 인터페이스 변경
    • get(int) 메서드 추가
public interface ProjectService {
  int delete(int projectNo) throws Exception;
  int add(Project project) throws Exception;
  List<Project> list(String keyword) throws Exception;

  // ProjectListCommand가 Map 객체를 받는다?
  // List 오버로딩한다.
  //메서드의 파라미터 개수나 파라미터 타입이 다르더라도
  // 같은 기능을 수행한다면
  // 같은 이름을 부여함으로서 프로그래밍의 일관성을 유지하게 도와주는 문법
  List<Project> list(Map<String,Object> keywords) throws Exception;
  Project get(int no) throws Exception;
}
  • com.eomcs.pms.service.DefaultProjectService 클래스 변경
    • get(int) 메서드 구현
public class DefaultProjectService implements ProjectService {\
.
.
.
	@Override
  public Project get(int no) throws Exception {
    return projectDao.findByNo(no);
  }
}
  • com.eomcs.pms.service.TaskService 인터페이스 생성
    • listByProject(int) 메서드 추가
public interface TaskService {
  List<Task> listByProject(int projectNo) throws Exception;
}
  • com.eomcs.pms.service.DefaultTaskService 클래스 생성
    • listByProject(int) 메서드 구현
public class DefaultTaskService implements TaskService {
  TaskDao taskDao;

  public DefaultTaskService(TaskDao taskDao) {
    this.taskDao = taskDao;
  }

  @Override
  public List<Task> listByProject(int projectNo) throws Exception {
    HashMap<String,Object> map = new HashMap<>();
    map.put("projectNo", projectNo);
    return taskDao.findAll(map);
  }
}
  • com.eomcs.pms.handler.ProjectDetailCommand 클래스 변경
    • ProjectService.get(int) 메서드를 사용하여 프로젝트를 조회한다.
    • TaskService.listByProject(int) 메서드를 사용하여 작업 목록을 조회한다.
public class ProjectDetailCommand implements Command {
  ProjectService projectService;
  TaskService taskService;

  public ProjectDetailCommand(ProjectService projectService, TaskService taskService) {
    this.projectService = projectService;
    this.taskService = taskService;
  }

  @Override
  public void execute(Map<String,Object> context) {
    System.out.println("[프로젝트 상세보기]");
    int no = Prompt.inputInt("번호? ");

    try {
      Project project = projectService.get(no);
      if (project == null) {
        System.out.println("해당 번호의 프로젝트가 존재하지 않습니다.");
        return;
      }

      System.out.printf("프로젝트명: %s\n", project.getTitle());
      System.out.printf("내용: %s\n", project.getContent());
      System.out.printf("기간: %s ~ %s\n",
          project.getStartDate(),
          project.getEndDate());
      System.out.printf("관리자: %s\n", project.getOwner().getName());
      System.out.print("팀원: ");
      project.getMembers().forEach(
          member -> System.out.print(member.getName() + " "));
      System.out.println();

      System.out.println("작업:");
      System.out.println("--------------------------------");

      HashMap<String,Object> map = new HashMap<>();
      map.put("projectNo", project.getNo());

      List<Task> tasks = taskService.listByProject(no);

      System.out.println("번호, 작업내용, 마감일, 작업자, 상태");
      for (Task task : tasks) {
        String stateLabel = null;
        switch (task.getStatus()) {
          case 1:
            stateLabel = "진행중";
            break;
          case 2:
            stateLabel = "완료";
            break;
          default:
            stateLabel = "신규";
        }
        System.out.printf("%d, %s, %s, %s, %s\n",
            task.getNo(),
            task.getContent(),
            task.getDeadline(),
            task.getOwner().getName(),
            stateLabel);
      }

    } catch (Exception e) {
      System.out.println("프로젝트 조회 중 오류 발생!");
      e.printStackTrace();
    }
  }
}

8단계 - 프로젝트 변경 커맨드에서 비즈니스 로직을 분리한다.

  • com.eomcs.pms.service.ProjectService 인터페이스 변경
    • update(Project) 메서드 추가
public interface ProjectService {
  int delete(int projectNo) throws Exception;
  int add(Project project) throws Exception;
  List<Project> list(String keyword) throws Exception;

  // ProjectListCommand가 Map 객체를 받는다?
  // List 오버로딩한다.
  //메서드의 파라미터 개수나 파라미터 타입이 다르더라도
  // 같은 기능을 수행한다면
  // 같은 이름을 부여함으로서 프로그래밍의 일관성을 유지하게 도와주는 문법
  List<Project> list(Map<String,Object> keywords) throws Exception;
  Project get(int no) throws Exception;
  int update(Project project) throws Exception;
}
  • com.eomcs.pms.service.DefaultProjectService 클래스 변경
    • update(Project) 메서드 구현
public class DefaultProjectService implements ProjectService {
.
.
.
	@Override
  public int update(Project project) throws Exception {
    return projectDao.update(project);
  }
}
  • src/main/resources/com/eomcs/pms/mapper/ProjectMapper.xml 변경
    • update SQL 문을 동적 SQL로 변경한다.
  <update id="update" parameterType="project">
    update pms_project
    <set>
    <if test="title != null">title = #{title},</if>
    <if test="title != null">content = #{content},</if>
    <if test="title != null">sdt = #{startDate},</if>
    <if test="title != null">edt = #{endDate},</if>
    </set>
    where no = #{no}
  </update>
  • com.eomcs.pms.handler.ProjectUpdateCommand 클래스 변경
    • ProjectService.update(Project) 메서드를 사용하여 프로젝트를 변경한다.
package com.eomcs.pms.handler;

import java.sql.Date;
import java.util.Map;
import com.eomcs.pms.domain.Project;
import com.eomcs.pms.service.ProjectService;
import com.eomcs.util.Prompt;

public class ProjectUpdateCommand implements Command {
  ProjectService projectService;

  public ProjectUpdateCommand(ProjectService projectService) {
    this.projectService = projectService;
  }

  @Override
  public void execute(Map<String,Object> context) {
    System.out.println("[프로젝트 변경]");
    int no = Prompt.inputInt("번호? ");

    try {
      Project project = projectService.get(no);
      if (project == null) {
        System.out.println("해당 번호의 프로젝트가 존재하지 않습니다.");
        return;
      }

      String value = Prompt.inputString(String.format(
          "프로젝트명(%s)? ", project.getTitle()));
      if (value.length() > 0) {
        project.setTitle(value);
      }

      value = Prompt.inputString(String.format(
          "내용(%s)? ", project.getContent()));
      if (value.length() > 0) {
        project.setContent(value);
      }

      value = Prompt.inputString(String.format(
          "시작일(%s)? ", project.getStartDate()));
      if (value.length() > 0) {
        project.setStartDate(Date.valueOf(value));
      }

      value = Prompt.inputString(String.format(
          "종료일(%s)? ", project.getEndDate()));
      if (value.length() > 0) {
        project.setEndDate(Date.valueOf(value));
      }

      String response = Prompt.inputString("정말 변경하시겠습니까?(y/N) ");
      if (!response.equalsIgnoreCase("y")) {
        System.out.println("프로젝트 변경을 취소하였습니다.");
        return;
      }

      if (projectService.update(project) == 0) {
        System.out.println("해당 번호의 프로젝트가 존재하지 않습니다.");
      } else {
        System.out.println("프로젝트를 변경하였습니다.");
      }
    } catch (Exception e) {
      System.out.println("프로젝트 변경 중 오류 발생!");
      e.printStackTrace();
    }
  }
}

9단계 - 게시글, 회원, 작업 커맨드 객체의 비즈니스 로직을 서비스 객체로 분리한다.

Categories:

Updated: