React

[ React ] TodoList 4️⃣ 할일 o,x표시 / 추가 / 제거

Mungwang 2023. 9. 13. 09:14

💎 TodoList 3️⃣ 

- TodoList 3️⃣ 에 이어서 진행하는거라 참고하자!

 

[ React ] TodoList3️⃣ 로그인/로그아웃

💎 React code 📣 App.js - Login 처리를 위한 컴포넌트를 생성해주고 improt를 진행한다. - export const TodoListContext = createContext() : export 내보내기를 통해 TodoListContext를 전역변수로 사용하겠다!! : ContextApi

mungwang.tistory.com

💎 App.js

- {loginMember && (<TodoList/>)} : 로그인멤버가 담겨있으면 TodoList컴포넌트를 실행할거다.

 
  import React, { useState, createContext } from 'react';
  import './App.css';

  import SignupContainer from './Signup';
  import Login from './Login';
  import TodoList from './TodoList';

  export const TodoListContext = createContext(); // 전역변수 생성

  function App() {
    // 회원가입, 로그인, 회원의 Todo List출력/추가/제거
    const [ signupView, setSignupView ] = useState(false);

    // 로그인한 회원 정보 저장
    const [ loginMember, setLoginMember] = useState(null);

    // 로그인한 회원의 todo-list를 저장
    const [ todoList, setTodoList ] = useState([]);

    return (
      <TodoListContext.Provider value={ {setTodoList, setLoginMember, loginMember, todoList} }>
        <button onClick={()=>{setSignupView(!signupView)}}>
          {signupView ?('회원 가입 닫기') : ('회원가입 열기')}
        </button>

        <div className='signup-wrapper'>
          {/* signupView가 true인 경우에만 회원 가입 컴포넌트 렌더링 */}
          {/* 조건식 && true인 경우 */}
          {signupView === true && (<SignupContainer/>)}
        </div>

        <h1>Todo List</h1>
        <Login/>

        <hr/>
 
        // 9-13 추가
        {loginMember && (<TodoList/>)}

      </TodoListContext.Provider>


 
    );
  }

  export default App;
 

💎 TodoList.js

- <li key={keyIndex++}> : let keyIndex =0; 라는 변수를 생성해서 key값으로 넣어준다

** React는 임의변수를 만들어 key값으로 사용하는걸 권장함! ** 

 

🔔 배열.splice(인덱스,몇칸) :: 배열의 인덱스 번째 요소부터 몇칸을 잘라내서 반환할지 지정

🔔 배열.map :: 기존 배열을 이용해서 새로운 배열 만들기

   
    import React, { useState, useContext } from 'react';
    import { TodoListContext } from './App';

    const TodoList = () => {

        const { setTodoList ,loginMember, todoList } = useContext(TodoListContext);
        const [inputTodo, setInputTodo] = useState('');
 
        let keyIndex = 0;

        // 할일 추가
        const handleAddTodo = () =>{

            // 입력 X
            if(inputTodo.trim().length === 0){
                alert("할 일을 입력해주세요");
                return;
            }

            fetch('/todo',{
                method : 'post',
                headers :  {'Content-Type' : 'application/json'},
                body : JSON.stringify({
                    title : inputTodo,
                    todoMemberNo : loginMember.todoMemberNo
                })
            })
            .then(resp => resp.text())
            .then(todoNo =>{

                if(Number(todoNo) === 0){ // 실패시 멈춤
                    return;
                }

                // 기존 todoList + 새로 추가된 Todo를 이용해
                // 새 배열을 만들어
                // todoList에 대입

                // 새로 추가된 Todo 만들기
                const newTodo = {
                    todoNo : todoNo,
                    title : inputTodo,
                    isDone : 'X',
                    todoMemberNo : loginMember.todoMemberNo
                };
                const newTodoList = [...todoList,newTodo];

                setTodoList(newTodoList);
                setInputTodo("");
            })
            .catch(e => console.log(e))

        }

        // O, X 업데이트
        const handleToggleTodo = (todo,index) =>{
            console.log(todo);
            console.log(index);
           
            fetch('/todo',{
                method : 'put',
                headers :  {'Content-Type' : 'application/json'},
                body : JSON.stringify({
                    todoNo : todo.todoNo,
                    isDone : todo.isDone === 'O'? 'X' : 'O'
                })
            })
            .then(resp => resp.text())
            .then( result =>{
                if(Number(result) === 0){ // 실패시 멈춤
                    console.log('업데이트 실패');
                    return;
                }
                   
                // 수정 성공시 todoList 값을 변경해서 리렌더링
                // todoList를 깊은 복사(똑같은 배열을 하나 더 만듬)
                const newTodoList = [...todoList];
       
                // index번째 요스의 O,X를 반대로 변경
                newTodoList[index].isDone = newTodoList[index].isDone === 'O'? 'X' : 'O';
               
                setTodoList(newTodoList);

            })
            .catch(e => console.log(e));
           
        }

        // 삭제
        const handleDeleteTodo = (todoNo,index) =>{
            console.log(todoNo)
       
            fetch('/todo',{
                method : 'delete',
                headers :  {'Content-Type' : 'application/json'},
                body : todoNo
            })
            .then(resp => resp.text())
            .then( result =>{

                if(Number(result) === 0){ // 실패시 멈춤
                    console.log('업데이트 실패');
                    return;
                }

                const deleteTodoList = [...todoList];

                deleteTodoList.splice(index,1);

                setTodoList(deleteTodoList);

            })
            .catch(e => console.log(e));

        }


        return(
            <>
                <h1>{loginMember.name}의 Todo List</h1>
                <div className="todo-container">

                    <h3>할 일(Todo) 입력</h3>
                    <div>
                        <input type="text" value={inputTodo} onChange={e => setInputTodo(e.target.value)} />
                        <button onClick={handleAddTodo}>Todo 추가</button>
                    </div>

                    <ul>
                        {todoList.map((todo, index) => (
                            <li key={keyIndex++}>
                                <div>
                                    <span className={todo.isDone === 'O' ? 'todo-compleate' : ''}> {todo.title} </span>

                                    <span>
                                        <button onClick={() => { handleToggleTodo(todo, index) }}>{todo.isDone}</button>
                                        <button onClick={() => { handleDeleteTodo(todo.todoNo, index) }}>삭제</button>
                                    </span>
                                </div>
                            </li>
                        ))}
                    </ul>

                </div>
           
            </>
        )
    };

    export default TodoList;
 

💎 Spring

📣 Dto

@Getter
@Setter
@ToString
public class Todo {
	 
	private int todoNo;
	private String title;
	private String isDone;
	private int todoMemberNo;
	
}

📣 Controller

@RestController
public class TodoController {
   
   // org.slf4j.Logger : 로그를 작성할 수 있는 객체
   // org.slf4j.LoggerFactory
   private Logger logger = LoggerFactory.getLogger(TodoController.class);
   
   @Autowired
   private TodoService service;   
   
   @PostMapping("/todo")
   public int insert(@RequestBody Todo todo) {
      return service.insert(todo);
   }
   
   
   @PutMapping("/todo")
   public int update(@RequestBody Todo todo) {
      return service.update(todo);
   }
   
   
   @DeleteMapping("/todo")
   public int delete(@RequestBody int todoNo) {
      return service.delete(todoNo);
   }
}

📣 Service

public interface TodoService {

   int insert(Todo todo);

   int update(Todo todo);

   int delete(int todoNo);
}

📣 ServiceImpl

@Service
public class TodoServiceImpl implements TodoService{

   @Autowired
   private TodoDao dao;
   
   @Transactional(rollbackFor = Exception.class)
   @Override
   public int insert(Todo todo) {
      int result = dao.insert(todo);
      return result > 0 ? todo.getTodoNo() : 0;
   }

   @Transactional(rollbackFor = Exception.class)
   @Override
   public int update(Todo todo) {
      return dao.update(todo);
   }

   @Transactional(rollbackFor = Exception.class)
   @Override
   public int delete(int todoNo) {
      return dao.delete(todoNo);
   }
}

📣 Dao

@Repository
public class TodoDao {
   
   @Autowired
   private SqlSessionTemplate sqlSession;

    public int insert(Todo todo) {
      return sqlSession.insert("todoMapper.insert", todo);
   }

   public int update(Todo todo) {
      return sqlSession.update("todoMapper.update", todo);
   }

   public int delete(int todoNo) {
      return sqlSession.delete("todoMapper.delete", todoNo);
   }
}

📣 Mapper

   <insert id="insert" useGeneratedKeys="true">
      
      <selectKey order="BEFORE" keyProperty="todoNo" resultType="_int" >
         SELECT SEQ_TODO_NO.NEXTVAL FROM DUAL
      </selectKey>
      
      INSERT INTO TODO_LIST
      VALUES(${todoNo}, #{title}, default, ${todoMemberNo})
   </insert>
   
   <update id="update">
      UPDATE TODO_LIST SET
      IS_DONE = #{isDone}
      WHERE TODO_NO = ${todoNo}
   </update>
   
   <delete id="delete">
      DELETE FROM TODO_LIST
      WHERE TODO_NO = ${todoNo}
   </delete>