08_Spring_240404(목)_58일차(0) - Spring Boot - todoList - 할 일 목록, 완료 할 일 개수, 할 일 추가

soowagger·2024년 4월 4일

8_Spring

목록 보기
7/38

1. 할 일 목록 + 완료한 할 일 개수 조회

MainController

@Slf4j // 로그 객체 자동 생성
@Controller // 요청/응답 제어 역할 명시 + Bean 등록
public class MainController {
	
	@Autowired // DI (의존성 주입) - 상속관계이거나 같은 타입
	private TodoService service;
	
	@RequestMapping("/") // 최상위(메인 페이지 요청)
	public String mainPage(Model model) {
		
		// 의존성 주입(DI) 확인(진짜 Service 객체 들어옴!)
		log.debug("service : " + service);
		
		// Service 메서드 호출 후 결과 반환 받기 
		// 목록조회, 카운트 결과 2개 기능 수행하고 싶음
		// --> Map 사용
		Map<String, Object> map = service.selectAll();
		
		// map에 담긴 내용 추출
		List<Todo> todoList = (List<Todo>)map.get("todoList");
		int completeCount = (int)map.get("completeCount");
		
		// Model : 값 전달용 객체(request scope) + session 변환 가능
		model.addAttribute("todoList", todoList);
		model.addAttribute("completeCount", completeCount);
		
		return "common/main"; // templates/common/main.html -> 이쪽으로 forward
	}
}

TodoService(Interface)

public interface TodoService {
	
	
	/** 할 일 목록 + 완료된 할 일 개수 조회
	 * @return map
	 */
	Map<String, Object> selectAll();

}

TodoServiceImpl

@Service // 비즈니스 로직 처리(데이터 가공, 트랜잭션 처리) 역할 명시 + Bean 등록
public class TodoServiceImpl implements TodoService {
	
	@Autowired
	private TodoMapper mapper;

	// 할 일 목록 + 완료된 할 일 개수 조회
	@Override
	public Map<String, Object> selectAll() {
		
		// 1. 할 일 목록 조회
		List<Todo> todoList = mapper.selectAll();
		
		// 2. 완료된 할 일 개수 조회
		int completeCount = mapper.getCompleteCount();

		// 3. Map으로 묶어서 반환
		Map<String, Object> map = new HashMap<>();
		
		map.put("todoList", todoList);
		map.put("completeCount", completeCount);
		
		return map;
	}
}

TodoMapper(Interface)

/* @Mapper
 * - MyBatis 제공 어노테이션
 * - 해당 어노테이션이 작성된 인터페이스는
 *   namespace에 해당 인터페이스가 작성된
 *   mapper.xml 파일과 연결되어 SQL 호출/수행/결과 반환 가능!
 * 
 * - MyBatis에서 제공하는 Mapper 상속 객체가 Bean 등록됨.
 */
@Mapper
public interface TodoMapper {
	
	/* Mapper의 메서드명 == mapper.xml 파일 내 태그의 id
	 * - 메서드명과 id가 같은 태그가 서로 연결됨
	 * 
	 */
	
	
	/** 할 일 목록 조회
	 * @return todoList
	 */
	List<Todo> selectAll();
	
	
	/** 완료된 할 일 개수 조회
	 * @return completeCount
	 */
	int getCompleteCount();	
}

Todo(DTO)

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Todo {
	private int todoNo; 			// 할 일 번호 
	private String todoTitle; 		// 할 일 제목 
	private String todoContent; 	// 할 일 내용
	private String complete; 		// 할 일 완료 여부("Y" / "N")
	private String regDate;			// 할 일 등록일(String으로 변환)
}

todo-mapper.xml

<mapper namespace="edu.kh.todo.model.mapper.TodoMapper">
  	<!-- Mapper 인터페이스 경로 작성(필수!!) 패키지명.인터페이스명 -->
  
  
	<!-- 아래 태그는 mapper.xml 파일 만들자마자 삭제! -->
	<!-- <cache-ref namespace=""/> -->
	
	
	<!-- 수행 하려는 SQL 구문 태그 작성-->


	<!-- <select> 태그 속성
		
		- id : 태그를 식별하는 값
		  (연결된 Mapper의 메서드명과 동일하게 작성해서 연결)
	
		- resultType : 조회 결과 한 줄(1행)을 저장하여 
					   반환할 변수/객체 자료형 지정
					   
					   조회 결과가 여러 행이면
					   행 별로 변수/객체 자료형으로 저장 후
					   모아서 List로 변환해서 반환  
	 -->	
	
	<!-- 
		edu.kh.todo.model.mapper.TodoMapper 인터페이스의
		selectAll() 메서드와 연결
		=> selectAll() 메서드 호출 시 연결된 SQL 실행
	
	
	    * mybatis-config.xml 안에 
		  <setting name="mapUnderscoreToCamelCase" value="true" />
		  구문이 있기 때문에 값을 얻어올 수 있음
		
		* DBConfig에서 별칭 등록을 해두었기 때문에 Todo라고 써도 된다.
		  => sessionFactoryBean.setTypeAliasesPackage("edu.kh.todo") 
	-->
	<!-- 할 일 목록 조회 -->
	<select id="selectAll" resultType="edu.kh.todo.model.dto.Todo">
		SELECT TODO_NO, TODO_TITLE, TODO_CONTENT, COMPLETE,
		TO_CHAR(REG_DATE, 'YYYY-MM-DD HH24:MI:SS') REG_DATE
		FROM TB_TODO
		ORDER BY TODO_NO
	</select>
  	
  	
  	<!-- Java의 int는 MyBatis의 _int -->
  	<!-- 완료된 할 일 개수 조회 -->
  	<select id="getCompleteCount" resultType="_int">
  		SELECT COUNT(*)
  		FROM TB_TODO
  		WHERE COMPLETE = 'Y'
  	</select>
</mapper>

main.html

<h1>TodoList-boot</h1>

<!-- ${#lists.size(todoList)} == ${todoList.size()} -->
<h3 th:text="|전체 Todo 개수 : ${#lists.size(todoList)}개 /
    완료된 Todo 개수 : ${completeCount}개|">내용</h3>

log.debug("service : " + service); 의존성 주입(DI) 확인 결과


2. 할 일 추가

인터페이스 메서드 부분 생략

TodoController

@Controller
@RequestMapping("todo") // "/todo"로 시작하는 모든 요청 매핑
public class TodoController {
	
	@Autowired // 같은 타입/상속관계 Bean 의존성 주입(DI)
	private TodoService service;
	
	
	@PostMapping("add") // "/todo/add" Post 방식 요청 매핑
	public String addTodo(
			@RequestParam("todoTitle") String todoTitle,
			@RequestParam("todoContent") String todoContent,
			RedirectAttributes ra) {
		// RedirectAttributes : 리다이렉트 시 값을 1회성으로 전달하는 객체
		// RedirectAttributes.addFlashAttribute("key", value) 형식으로 잠깐 세션에 속성을 추가
		
		// [원리]
		// 응답 전     : request scope
		// redirect 중 : session scope로 이동
		// 응답 후     : request scope 복귀 
		
		
		// 서비스 메서드 호출 후 결과 반환 받기
		int result = service.addTodo(todoTitle, todoContent);
		
		// 삽입 결과에 따라 message 값 지정
		String message = null;
		
		if(result > 0) message = "할 일 추가 성공!!";
		else message = "할 일 추가 실패..ㅠㅠ";
		
		// 리다이렉트 후 1회성으로 사용할 데이터를 속성으로 추가
		ra.addFlashAttribute("message", message);
		
		return "redirect:/"; // 메인페이지 재요청 - HTML 주소가 아님!!!!!!!!!
	}
}

TodoServiceImpl

// 할 일 추가
@Override
public int addTodo(String todoTitle, String todoContent) {
	
	// Connection 생성 / 반환 X
	// 트랜잭션 제어 처리 -> @Transactional 어노테이션
	
	
	// MyBatis에서 SQL에 전달할 수 있는 파라미터의 개수는 
	// 오직 1개
	// -> todoTitle, todoContent를 Todo DTO로 묶어서 전달
	Todo todo = new Todo();
	todo.setTodoTitle(todoTitle);
	todo.setTodoContent(todoContent);
	
	return mapper.addTodo(todo);
}

todo-mapper.xml

 	<!-- parameterType : 전달 받은 값(전달 인자/매개변수)의 타입을 명시 
 		-> DTO, Map인 경우 필드명/key를 입력해서 하나씩 꺼낼 수 있음
 	-->
 	
 	<!-- 
 		mapper에서 Java의 data(파라미터)를 SQL에 삽입하는 방법
 		 
 		1) #{변수명 | 필드명} : SQL에 삽입 시 양쪽에 '' 붙여서 삽입
 		
 		2) ${변수명 | 필드명} : SQL에 삽입 시 양쪽에 '' 붙이지 않고 삽입
 		
 		-> ${} 언제 사용할까?
 		1) 삽입할 값이 숫자인 경우 (숫자 리터럴에 '' 없음)
 		2) SQL문 자체가 변해야 하는 경우에 사용
 		
 		ex) SELECT MEMBER_NAME, ${key} FROM TB_MEMBER
 		
 		- ${key} == ENROLL_DATE인 경우
 		SELECT MEMBER_NAME, ENROLL_DATE FROM TB_MEMBER
 		
 		- ${Key} == MEMBER_ADDRESS인 경우
 		SELECT MEMBER_NAME, MEMBER_ADDRESS FROM TB_MEMBER
 	-->
 	
 	<!-- 할 일 추가 -->
 	<insert id="addTodo" parameterType="Todo">
 	INSERT INTO TB_TODO
	VALUES(SEQ_TODO_NO.NEXTVAL, #{todoTitle}, #{todoContent}, DEFAULT, DEFAULT )
 	</insert>

main.html 구문 추가

<form action="/todo/add" method="post">
    <h4>할 일 추가</h4>

    <div>
        제목 : <input type="text" name="todoTitle">
    </div>

    <div>
        <textarea name="todoContent" cols="50" rows="5" placeholder="상세 내용"></textarea>
    </div>

    <button>추가하기</button>
</form>
<!-- footer.html 조각을 해당 위치에 대입
    조각의 경로는 forward 하듯이 접두사, 접미사 생략하고 작성 -->
<th:block th:replace="~{common/footer}"></th:block>

footer.html (메시지 알림창)

<!-- 스크립트 태그의 경우 바디 태그 하단에 작성하기 때문에 footer-->

<!-- message 값 alert 띄워줄 script 작성 -->
<script th:inline="javascript">
    
    const message = /*[[${message}]]*/ "전달받은 메세지";
                    // th:inline="javascript" 
                    // 전달받은 message가 없으면 null

    if(message != null) {
        alert(message);
    }

</script>

profile

0개의 댓글