자바풀스택 과정/자바 풀 스택 : 수업내용정리

자바 풀 스택 3/18 하루 기록 076

파티피플지선 2025. 3. 18. 16:33

9:29 학원 도착

 

 

 

 

 

<9:30 1교시>

주소 창의 #은 주소창의 ID 같은 것이라 생각하면 된다..?라고 하는 거 같은데

예를 들어 localhost:9000/home에 대한 요청을 할 때, 그 home에는 html 요소의 id가 one, two, three인 div 같은 것이 있을 수 있는데, localhost:9000/home#one이라고 하면 그 페이지 내에서의 책갈피로서 스크롤 이동을 가능하게 해주는데,

이런 관점에서 봤을 때 

localhost:3000/#/posts?pageNum=1이라고 했을 때 웹브라우저 입장에서는 이게 localhost:3000이라는 최상위 경로를 요청했고, 그에 대해 id가 posts?pageNum=1인 콘텐츠를 보여주고 있는것이라고 이해하면 됨.

 

src의 pages에 post.jsx가 있는데 여기서 첨으로 useSearchParams라는 훅을 사용했다. 

useSearchParams({pageNum:1, condition:"", keyword:""})라는 식

이것은 마치 ?pageNum=1&condition=keyword=이라고 초기값을 넣어준 것과 같다.

pageNum을 알고 싶다면 params.get("pageNum")이라고 작성하면 된다.

 

useEffect 함수는 Post컴포넌트가 활성화 되는 시점에 한번 호출되고 또한 params에 변화가 생겼을 때 호출된다.

 

 

보이는 글을 마크업 형식으로 해석해주는 코드

 

 

파일이름에 module이 들어가 있는 애들은 import 한 곳에서만 선택적으로 사용하겠다는 의미.

 

 

 

 

 

<10:30 2교시>

 

 

<11:30 3교시>

alert창이나 confirm 창을 components로서 관리하려고 함.

 

 

 

<12:30 4교시>

혼자 힘으로 PostUpdate만들어보기 중

삽질 과정

더보기

밥먹기 전

import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import { Button, FloatingLabel, Form } from 'react-bootstrap';
import { useSelector } from 'react-redux';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import AlertModal from '../components/AlertModal';
import { initEditor } from '../editor/SmartEditor';

function PostUpdate(props) {
    // "/posts/:num" 에서 num 에 해당되는 경로 파라미터 값 읽어오기
    const {num} = useParams()
    //글 하나의 정보를 상태값으로 관리
    const [state, setState]=useState({})
    //검색 키워드 관련처리
    const [params, setParams]=useSearchParams({
        condition:"",
        keyword:""
    });
    //로그인된 userName
    const userName = useSelector(state => state.userInfo.userName);


    //SmartEditor 에 작성한 내용을 textarea 의 value 로 넣어 줄때 필요한 함수가 editorTool 이다 
    const [editorTool, setEditorTool] = useState([])

    useEffect(()=>{
        //initEditor() 함수를 호출하면서 SmartEditor 로 변환할 textarea 의 id 를 전달하면
		//textarea 가 SmartEditor 로 변경되면서 에디터 tool 객체가 리턴된다.  
		setEditorTool(initEditor("content")); // initEditor() 함수를 호출해야 SmartEditor 가 초기화된다.
    }, [])

    //입력한 내용을 얻어오기 위한 useRef()  
    const inputTitle=useRef()
    const inputContent=useRef()
    //경로 이동을 할 함수 
    const navigate = useNavigate()
    //알림 모달을 띄울지 말지를 state로 관리하기
    const [modalShow, setModalShow]=useState(false);

    useEffect(()=>{
        const query=new URLSearchParams(params).toString();
        axios.get(`/posts/${num}${params.get("condition") ? "?"+query : ""}`)
        .then(res=>{
            //글하나의 정보를 상태값에 넣어주고 
            setState(res.data);
        })
        .catch(error=>{
            console.log(error);
        });

    }, []);


    return (
        <>
            <AlertModal show={modalShow} message="글이 수정되었습니다" onYes={()=>{
                navigate(`/posts/${state.num}`)
                setModalShow(false);
            }}/>
            <h1>글 수정하기</h1>
            <Form>
                <FloatingLabel label="제목" className="mb-3" controlId="title">
                    <Form.Control ref={inputTitle} type="text">{state.title}</Form.Control>
                </FloatingLabel>
                <Form.Group className="mb-3"  controlId="content">
                    <Form.Label>내용</Form.Label>
                    <Form.Control ref={inputContent} as="textarea" rows="10"><div dangerouslySetInnerHTML={{__html:state.content}}></div></Form.Control> 
                </Form.Group> 
                <Button type="submit" onClick={(e)=>{
                    //폼 제출 막기
                    e.preventDefault();
                    //에디터 tool 을 이용해서 SmartEditor 에 입력한 내용을 textarea 의 value 값으로 변환
                    editorTool.exec();
                    //입력한 제목과 내용을 읽어와서
                    const title=inputTitle.current.value;
                    const content=inputContent.current.value;
                    //axios 를 이용해서 api 서버에 전송
                    axios.patch(`/posts/${state.num}`, {title, content})
                    .then(res=>{
                        setModalShow(true)
                    })
                    .catch(error=>{
                        console.log(error);
                    });
                }}>저장</Button>
            </Form>
        </>
    );
}

export default PostUpdate;

 

 

점심 먹고 해본거

import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import { Button, FloatingLabel, Form } from 'react-bootstrap';
import { useSelector } from 'react-redux';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import AlertModal from '../components/AlertModal';
import { initEditor } from '../editor/SmartEditor';

function PostUpdate(props) {
    // "/posts/:num" 에서 num 에 해당되는 경로 파라미터 값 읽어오기
    const {num} = useParams()
    //글 하나의 정보를 상태값으로 관리
    const [state, setState]=useState({})
    //검색 키워드 관련처리
    const [params, setParams]=useSearchParams({
        condition:"",
        keyword:""
    });
    //로그인된 userName
    const userName = useSelector(state => state.userInfo.userName);


    //SmartEditor 에 작성한 내용을 textarea 의 value 로 넣어 줄때 필요한 함수가 editorTool 이다 
    const [editorTool, setEditorTool] = useState([])

    useEffect(()=>{
        //initEditor() 함수를 호출하면서 SmartEditor 로 변환할 textarea 의 id 를 전달하면
		//textarea 가 SmartEditor 로 변경되면서 에디터 tool 객체가 리턴된다.  
		setEditorTool(initEditor("content")); // initEditor() 함수를 호출해야 SmartEditor 가 초기화된다.
    }, [])

    //입력한 내용을 얻어오기 위한 useRef()  
    const inputTitle=useRef()
    const inputContent=useRef()
    const onInput =()=>{
        setState({ ...state, title:inputTitle, content:inputContent})
    }

    //경로 이동을 할 함수 
    const navigate = useNavigate()
    //알림 모달을 띄울지 말지를 state로 관리하기
    const [modalShow, setModalShow]=useState(false);

    useEffect(()=>{
        const query=new URLSearchParams(params).toString();
        axios.get(`/posts/${num}${params.get("condition") ? "?"+query : ""}`)
        .then(res=>{
            //글하나의 정보를 상태값에 넣어주고 
            setState(res.data);
        })
        .catch(error=>{
            console.log(error);
        });

    }, []);


    return (
        <>
            <AlertModal show={modalShow} message="글이 수정되었습니다" onYes={()=>{
                navigate(`/posts/${state.num}`)
                setModalShow(false);
            }}/>
            <h1>글 수정하기</h1>
            <Form>
                <Form.Group className="mb-3" controlId="title">
                    <Form.Label>제목</Form.Label>
                    <Form.Control ref={inputTitle} type="text" onChange={onInput} placeholder={state.title}/>
                </Form.Group>
                <Form.Group className="mb-3"  controlId="content">
                    <Form.Label>내용</Form.Label>
                    <Form.Control ref={inputContent} as="textarea" onChange={onInput} rows="10" value={state.content}/> 
                </Form.Group> 
                <Button type="submit" onClick={(e)=>{
                    //폼 제출 막기
                    e.preventDefault();
                    //에디터 tool 을 이용해서 SmartEditor 에 입력한 내용을 textarea 의 value 값으로 변환
                    editorTool.exec();
                    //입력한 제목과 내용을 읽어와서
                    const title=inputTitle.current.value;
                    const content=inputContent.current.value;
                    //axios 를 이용해서 api 서버에 전송
                    axios.patch(`/posts/${state.num}`, {title, content})
                    .then(res=>{
                        setModalShow(true)
                    })
                    .catch(error=>{
                        console.log(error);
                    });
                }}>저장</Button>
            </Form>
        </>
    );
}

export default PostUpdate;

 

 

 

<14:30 5교시>

package.json에 jsontokens를 없애기

 

npmjs.com에서는 npm으로 설치할 수 있는 목록을 검색할 수 있다.

 

 

jwtDecode로 바꾸고 payload란 방이 없으니 바로 decoded.sub 등으로 바꾸기(App, loginModal,)

 



수정 컴포넌트 동작 시킬 준비 PostUpdateForm.jsx로 만들어주기. 나는 걍 PostUpdate라고 한거에 수정할 거 있으면 수정해야지. 점심시간에 기본적인건 해놨는데 수정 내용이 제대로 전달되지 않는게 아무래도 API 요청 부분을 잘못한 거 같음

 

샘이한거랑 내가 한 거 다른거 위주로 살며봐야지.


- 샘은 /post/:num/edit이라고 path를 정했음. 이게 나아보여서 내껏도 바꿈.

내가 한거

-샘은  onClick ={()=>{ navigate(`/posts/${state.num}/edit`)) }}이라고 작성하심

 

 

일단 함수를 호출하고 async와 await이 있을 때 promise가 해결될 때까지 기다린다.

then 함수에서 res 값에 들어오는 걸 await로 기다리고 있다가

응답이 오면 try~catch가 실행된다.

async await를 사용하는 장점은 const res=await ~~ 형식을 여러줄을 연달아 쓸 수 있다는 것이 장점이다.

응답을 받아올 것이 여러개일 때 사용하기 용이하다는 것.

 

 

샘이랑 한 코드

<15:30 6교시>

 

스프링부트 서버에 컨트롤러 설정하기

 

 

선생님 코드

// src/pages/PostUpdateForm.jsx

import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import { Button, FloatingLabel, Form } from 'react-bootstrap';
import { useNavigate, useParams } from 'react-router-dom';
import { initEditor } from '../editor/SmartEditor';
import AlertModal from '../components/AlertModal';

function PostUpdateForm(props) {
    //수정할 글번호 추출 "/posts/:num/edit" 에서 num 에 해당되는 경로 파라미터 값 읽어오기
    const {num} = useParams();

    //SmartEditor 에 작성한 내용을 textarea 의 value 로 넣어 줄때 필요한 함수가 editorTool 이다 
    const [editorTool, setEditorTool] = useState([])

    // component 가 활성화 되는 시점에 호출되는 함수 
    useEffect(()=>{
        // initEditor() 함수를 호출해야 SmartEditor 가 초기화 된다.
        setEditorTool(initEditor("content"));

        //비동기로 동작(호출하면 바로 리턴하지만 안에 있는 code 는 모두 실행된 상태가 아닌 비동기 함수)
        const fetchPost = async ()=>{
            try{
                const res = await axios.get(`/posts/${num}`);
                //글정보를 콘솔창에 출력하기
                console.log(res.data);
                //글 제목과 내용을  input 요소에 넣어주기 
                inputTitle.current.value=res.data.title;
                inputContent.current.value=res.data.content;
            }catch(error){
                console.log(error);
            }
        };
        fetchPost();
        console.log("fetchPost() 함수 호출함");
    }, []);

    //제목을 입력할 input type="text" 와  내용을 입력할 textarea 의 참조값을 관리하기 위해 
    const inputTitle=useRef();
    const inputContent=useRef();

    const handleSubmit = (e)=>{
        e.preventDefault();
        //에디터 tool 을 이용해서 SmartEditor 에 입력한 내용을 textarea 의 value 값으로 변환
        editorTool.exec();
        //입력한 제목과 내용을 읽어와서
        const title=inputTitle.current.value;
        const content=inputContent.current.value;
        //수정반영 요청을 하는 비동기 함수
        const updatePost = async ()=>{
            try{
                const res = await axios.patch(`/posts/${num}`, {title, content});
                console.log(res.data);
                //모달을 띄운다음 글자세히 보기로 이동되도록 한다.
                setShowModal(true);
            }catch(error){
                console.log(error);
            }
        };
        updatePost();
    }

    const [showModal, setShowModal] = useState(false);
    const navigate = useNavigate();
    return (
        <>
            <AlertModal show={showModal} message="수정했습니다" onYes={()=>{
                navigate(`/posts/${num}`);
            }}/>
            <h1>글 수정 양식 입니다</h1>
            <Form>
                <FloatingLabel label="제목" className='mb-3' controlId='title'>
                    <Form.Control ref={inputTitle} type="text"/>
                </FloatingLabel>
                <Form.Group className='mb-3' controlId='content'>
                    <Form.Label>내용</Form.Label>
                    <Form.Control ref={inputContent} as="textarea" style={{height:"300px"}}/>
                </Form.Group>
                <Button type="submit" onClick={handleSubmit}>수정확인</Button>
                <Button type="reset" variant='danger'>취소</Button>
            </Form>
        </>
    );
}

export default PostUpdateForm;

 

 

추가하는 기능 : 수정 취소를 누르면 원래 정보가 돌아오게 했어야 하는데 제목이 저장은 됐는데 자꾸 사라져있는게 버튼 타입이 reset이어서 그런걸 발견해서 handleReset에도 preventdefault를 추가할 수 있도록 샘을 도왔다 므하하

import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import { Button, Form } from 'react-bootstrap';
import { useNavigate, useParams} from 'react-router-dom';
import AlertModal from '../components/AlertModal';
import { initEditor } from '../editor/SmartEditor';

function PostUpdate(props) {
    // 수정할 글번호 추출"/posts/:num/edit" 에서 num 에 해당되는 경로 파라미터 값 읽어오기
    const {num} = useParams()

    //SmartEditor 에 작성한 내용을 textarea 의 value 로 넣어 줄때 필요한 함수가 editorTool 이다 
    const [editorTool, setEditorTool] = useState([])

    //원래 글의 내용을 state에 저장해 놓기
    const [savedData, setSavedData] = useState();

    //제목 입력할 input type="text"와 내용을 입력할 textarea의 참조값을 관리하기
    const inputTitle=useRef()
    const inputContent=useRef()

    //component가 활성화 되는 시점에 호출되는 함수
    useEffect(()=>{
        //initEditor()함수를 호출해야 SmartEditor가 초기화 된다.
        //initEditor() 함수를 호출하면서 SmartEditor 로 변환할 textarea 의 id 를 전달하면
		//textarea 가 SmartEditor 로 변경되면서 에디터 tool 객체가 리턴된다.  
		setEditorTool(initEditor("content")); // initEditor() 함수를 호출해야 SmartEditor 가 초기화된다.
        
        //비동기로 동작(호출하면 바로 리턴하지만 안에 있는 코드는 모두 실행된 상태가 아닌 비동기 함수)
        const fetchPost=async ()=>{
            try{
                const res=await axios.get(`/posts/${num}`)
                console.log(res.data)//글정보를 콘솔창에 출력
                inputTitle.current.value=res.data.title;
                inputContent.current.value=res.data.title;
                setSavedData(res.data);
            }catch(error){
                console.log(error)
            }            
        }
        fetchPost();
        console.log("fetch함수 호출함")//이 함수는 호출하면 바로 리턴한다 =console.log("fetchPost()함수를 호출함")
    }, [])


    const handleSubmit = (e)=>{
        //폼 제출 막기
        e.preventDefault();
        //에디터 tool 을 이용해서 SmartEditor 에 입력한 내용을 textarea 의 value 값으로 변환
        editorTool.exec();
        console.log("에디터툴툴")
        //입력한 제목과 내용을 읽어와서
        const title=inputTitle.current.value;
        const content=inputContent.current.value;
        console.log("데이터 읽기")
        //수정 반영 요청을 하는 비동기 함수
        const updatePost= async ()=>{
            try{
                const res=await axios.patch(`/posts/${num}`, {title,content})
        
                console.log(res.data)
                //모달 띄우기
                setModalShow(true);
                //글 자세히 보기로 이동
            }catch(error){
                console.log(error)
            }
           
        }
        updatePost()
        console.log("업데이트포스트함수호출출")
        //axios 를 이용해서 api 서버에 전송
        // axios.patch(`/posts/${state.num}`, {title, content})
        // .then(res=>{
        //     setModalShow(true)
        // })
        // .catch(error=>{
        //     console.log(error);
        // });
    }

    //경로 이동을 할 함수 
    const navigate = useNavigate()
    //알림 모달을 띄울지 말지를 state로 관리하기
    const [modalShow, setModalShow]=useState(false);

    const handleReset = (e)=>{
        e.preventDefault();
        //title을 원상복구
        inputTitle.current.value=savedData.title;
        //smartEditor에 출력된 내용을 원상 복구
        editorTool.setContents(savedData.content);
    }


    return (
        <>
            <AlertModal show={modalShow} message="글이 수정되었습니다" onYes={()=>{
                navigate(`/posts/${num}`)
                setModalShow(false);
            }}/>
            <h1>글 수정하기</h1>
            <Form>
                <Form.Group className="mb-3" controlId="title">
                    <Form.Label>제목</Form.Label>
                    <Form.Control ref={inputTitle} type="text" />
                </Form.Group>
                <Form.Group className="mb-3"  controlId="content">
                    <Form.Label>내용</Form.Label>
                    <Form.Control ref={inputContent} as="textarea" rows="10"/> 
                </Form.Group> 
                <Button type="submit" onClick={handleSubmit}>수정 확인</Button>
                <Button type="reset" variant='danger' onClick={handleReset}>취소</Button>
            </Form>
        </>
    );
}

export default PostUpdate;

 

 

<16:30 7교시 ~8교시>

시험