48,400원
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 해결됨한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
content부분 초기화가 되지않아 질문 남깁니다!
안녕하세요 강사님 :)강의 수강 중, author, emotion부분은 저장 이후 초기화가 되나, content부분은 초기화가 되지 않습니다! 🥲 아무리 오타 있나...확인을 해보고 ㅠ_ㅠ..어떤 오류가 있는건지 찾아아도 더 이상 모르겠어서 글을 남깁니다! 샌드박스 포크(?) 주소를 함께 남겨요. 감사합니다! https://codesandbox.io/s/chapt-2-forked-dc3sm8
- 해결됨한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
Props 접근 방식 질문
안녕하세요. 섹션5 4번째 강의를 수강하면서 궁금한 게 생겨 질문글 남김니다.강의 영상 7:02 쯤에서 App.js에서 정의한 dummyList를 하위의 컴포넌트인 DiaryList에서 받아올 때 ({diaryList}) => {...}라고 객체를 직접적으로 명시해서 전달하는 방법을 확인하였습니다. 그런데 이 방식말고도 const DiaryList = (props) => {..} 이렇게 전달받아서 함수 내에서 props.diaryList로 리스트에 접근하는 것도 가능하다는 것을 알게 되었습니다.하위 컴포넌트에서 (props.전달인자명)으로 접근하는 것과 ({전달인자명})으로 접근하는 것의 차이가 있는 지 궁금합니다.
- 해결됨한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
React 실전프로젝트 질문
왜 강의와 다르게 이렇게 표시되는게 맞는지 굼금해서 올려봅니다.아니라면 왜 이런지 알려주시면 감사하겠습니다.밑에는 App.css입니다.@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap'); body { background-color: #f6f6f6; display: flex; justify-content: center; align-items: center; font-family: 'Nanum Pen Script'; min-height: 100vh; margin: 0px; } @media (min-width: 650px) { .App { width: 640px; } } @media (max-width: 650px) { .APP { width: 90vw; } } #root { background-color: white; box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; } .App { min-width: 100vh; padding-left: 20px; padding-left: 20px; } /* my button */ .MyButton { cursor: pointer; border: none; border-radius: 5px; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 20px; font-size: 18px; white-space: nowrap; font-family: 'Nanum Pen Script'; } .MyButton_default { background-color: #ececec; color: black; } .MyButton_positive { background-color: #64c964; color: white; } .MyButton_negative { background-color: #fd565f; color: white; } /* header */ header { padding-top: 20px; padding-bottom: 20px; display: flex; align-items: center; border-bottom: 1px solid #e2e2e2; } header > div { display: flex; } header .head_text { width: 50%; font-size: 25px; justify-content: center; } header .head_btn_left { width: 25%; justify-content: start; } header .head_btn_right { width: 25%; border-left: 25px; justify-content: end; } header button { font-family: 'Nanum Pen Script'; } /* DiaryList */ .DiaryList .menu_wrapper { margin-top: 20px; margin-bottom: 30px; display: flex; justify-content: space-between; } .DiaryList .menu_wrapper .right_col { flex-grow: 1; } .DiaryList .menu_wrapper .right_col button { width: 100%; } .DiaryList .ControlMenu { margin-right: 10px; border: none; border-radius: 5px; background-color: #ececec; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 20px; cursor: pointer; font-family: 'Nanum Pen Script'; font-size: 18px; } /* DiaryItem */ .DiaryItem { padding-top: 15px; padding-bottom: 15px; border-bottom: 1px solid #e2e2e2; display: flex; justify-content: space-between; } .DiaryItem .emotion_img_wrapper { cursor: pointer; min-width: 120px; height: 80px; border-radius: 5px; display: flex; justify-content: center; } .DiaryItem .emotion_img_wrapper_1 { background-color: #64c964; } .DiaryItem .emotion_img_wrapper_2 { background-color: #9dd772; } .DiaryItem .emotion_img_wrapper_3 { background-color: #fdce17; } .DiaryItem .emotion_img_wrapper_4 { background-color: #fd8446; } .DiaryItem .emotion_img_wrapper_5 { background-color: #fd565f; } .DiaryItem .emotion_img_wrapper img { width: 50%; } .DiaryItem .info_wrapper { flex-grow: 1; margin-left: 20px; cursor: pointer; } .DiaryItem .diary_date { font-weight: bold; font-size: 25px; margin-bottom: 5px; } .DiaryItem .diary_centent_preview { font-size: 18px; } .DiaryItem .btn_wrapper { min-width: 70px; } /* DirayEditor */ .DirayEditor textarea { font-family: 'Nanum Pen Script'; font-size: 20px; box-sizing: border-box; width: 100%; min-height: 200px; resize: vertical; border: none; border-radius: 5px; background-color: #ececec; padding: 20px; } .DirayEditor .control_box { display: flex; justify-content: space-between; align-items: center; } .DirayEditor section { margin-bottom: 40px; } .DirayEditor h4 { font-size: 22px; font-weight: bold; } .DirayEditor .input_date { border: none; border-radius: 5px; background-color: #ececec; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 20px; cursor: pointer; font-family: 'Nanum Pen Script'; font-size: 20px; } .DirayEditor .emotion_list_wrapper { display: grid; grid-template-columns: repeat(5, auto); gap: 2%; } /* EmotionItem */ .EmotionItem { cursor: pointer; border-radius: 5px; padding-top: 20px; padding-bottom: 20px; display: flex; flex-direction: column; justify-content: center; align-items: center; } .EmotionItem img { width: 50%; margin-bottom: 10px; } .EmotionItem span { font-size: 18px; } .EmotionItem_off { background-color: #ececec; } .EmotionItem_on_1 { background-color: #64c964; color: white; } .EmotionItem_on_2 { background-color: #9dd772; color: white; } .EmotionItem_on_3 { background-color: #fdce17; color: white; } .EmotionItem_on_4 { background-color: #fd8446; color: white; } .EmotionItem_on_5 { background-color: #fd565f; color: white; }
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
context api 사용시 ( ) 안에
createContext( ) 괄호 안에 데이터를 입력하면 어디에 쓰이나요?value에 기본값으로 해당 데이터가 들어 가는건가요?예를 들어 export const DiaryStateContext = React.createContext("HELLO"); 이런 식이면 저 HELLO를 어디서 꺼내서 쓸수 있을까요? const hello = useContext(DiaryStateContext);이런 식으로 해도 데이터가 들어 오는거 같진 않네요 ㅜㅜ
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
onClick 사용시 질문입니다!
onClick를 이용할경우 onClick= { ( ) => ?? } 해당 형식으로 사용해야하는 경우가 있고 아닌경우가 있는데 헷갈리네요 ㅜㅜ 클릭할떄 실행되는함수가 파라미터를 받지않는다면 onClick={함수명} 이런식이고 파라미터를 받는다면 onClick={( )=>함수명( 파라미터 )} 이런식인거같은데 다른경우도 있을까요?? 콜백 함수에 관한 영상을 찾아보면 이해가 될까요??
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
dummyData 받아오는 오류가 있습니다.
페이지 구현- 홈(/) 파트에서 17:00분 정도에 useEffect를 사용하여 data를 콘솔에 찍어보는데 그전까지는 home component까지는 더미데이터가 잘 받아와졌는데 저부분에서 콘솔에서 이렇게 빈배열로 넘어와져요계속 코드를 몇번씩이나 강의돌려보고 검토했지만 도저히 제눈엔 보이지않아 강사님께 질문드립니다.깃허브에 코드올려놨습니다. 한번 검토해주시면 감사하겠습니다!!https://github.com/eunsoo-cho/emotion-Diart.git
- 해결됨한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
만약에 글이 엄청 많을 때도 filter 사용해서 삭제하는지 궁금합니다.
선생님 안녕하세요!실제 서비스들은 누적된 글개수가 엄청 많은데, 실제 현업에서도 filter로 글삭제를 하는지 궁금합니다.글 개수가 엄청 많아지면 글 하나 삭제버튼 눌렀을 때 filter로 배열을 돌다가 로딩이 느려질 것 같다는 생각이 들어서요!
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
삭제하기 버튼은 왜 없나요?
DiaryEditor에서 뒤로가기랑 취소하기 버튼 2개가 있는데 "취소하기를 삭제하기로 바꿔야 하는 것 아닌가?"라는 의문이 들었습니다. 강의를 모든 페이지 구성까지 들었는데 어디에서 일기 삭제하기가 없어서요..혹시 책이나 뒷 강의에 나올진 모르겠지만..
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
날짜 형식
const getStringDate = (date) => { return date.toISOString().slice(0, 10); } const New = () => { const [date, steDate] = useState(getStringDate(new Date())); const navigate = useNavigate(); return ( <div> <MyHeader headText={"새 일기쓰기"} leftChild={<MyButton text={'< 뒤로 가기'} onClick={()=> navigate(-1)}/>} /> <div> <section> <h4>오늘은 언제인가요?</h4> <div className="input-box"> <input value={date} type="date" onChange={(e) => steDate(e.target.value)} /> </div> </section> </div> </div> ) }왜 저는 yyyy-mm-dd 형식이 아니라 yyyy.mm.dd로 나올까요? ㅠㅠ
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
date 설정을 바꿀 수 없을까요?
date를 나타내기 위해const getStringDate = (date) => { return date.toISOString().slice(0, 10); }사용헤서 date를 설정하니까밤에 작성하건 낮에 작성하건 Home페이지에서모두 같은 시간으로 저장되서 시간순으로 저장되는 느낌이 안사는 것 같아요.toISOstring을 안하고 onCreate에 데이터를 넣는 코드 구조도 알려주실 수 있나요? --------------------------------------스스로 생각을 해보았는데 혹시const handleSubmit = () => { if (content.length < 1) { contentRef.current.focus(); return; } onCreate({ date, content, emotion }); navigate("/", { replace: true }); };에서 onCreate 함수에 date대신 new Date().getTime()를 넣으면 제가 생각한 문제가 해결될까요? --------------------------------------다시 생각을 해보았는데 이렇게 하면 시간이 지났을때 전날 일기를 작성할 수가 없네요..
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
npx create-react-app 오류 문의드립니다
뭐가 문제일까요? ㅠㅠ
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
diaryList 날짜 필터 관련
안녕하세요. 저는 현재 "페이지구현-홈(/)" 강의 18분 쯔음 까지 들었습니다.Home 컴포넌트에서 filter 를 사용하여 현재 날짜에만 dummyList 로 작성해둔 일기가 떠야하는데 뜨질 않습니다.import { DiaryStateContext } from "./App"; import MyButton from "./components/MyButton"; import MyHeader from "./components/MyHeader"; import { useContext, useEffect, useState } from "react"; import DiaryList from "./components/DiaryList"; const Home = () => { const diaryList = useContext(DiaryStateContext); //dummyList 를 받음 const [data,setData] = useState(); const [curDate , setCurDate] = useState(new Date()); const headText= `${curDate.getFullYear()}년 ${curDate.getMonth() + 1}월`; useEffect(()=> { if (diaryList.length >= 1) { const firstDay = new Date( curDate.getFullYear(), curDate.getMonth(), 1 ).getTime(); //현재 년, 월의 1일의 시간을 구함 const lastDay = new Date( curDate.getFullYear(), curDate.getMonth() +1, 0, ).getTime(); setData(diaryList.filter((it) => firstDay <= it.date && it.date <= lastDay)); } },[diaryList, curDate]); useEffect(() => { console.log(data) console.log(diaryList) },[data]) const increaseMonth = () => { setCurDate(new Date( curDate.getFullYear() , curDate.getMonth() + 1, curDate.getDate() )) } const decreaseMonth = () => { setCurDate( new Date(curDate.getFullYear(), curDate.getMonth() - 1, curDate.getDate()) ); }; return ( <div> <MyHeader leftChild={<MyButton text={"<"} onClick={decreaseMonth} />} headText={headText} rightChild={<MyButton text={">"} onClick={increaseMonth} />} /> <DiaryList diaryList={data}/> </div> ); } export default Home;이게 제 Home 컴포넌트이고, 중간에 보면 useEffect 로 data 와 diaryList 를 출력했을때 data 는 빈 배열로 출력되고 diaryList 는 제가 App.js 에서 dummyList 로 작성한 데이터가 잘 출력됩니다. 아무리 봐도 어디가 잘못된건지 모르겠습니다. ㅜㅜ날짜가 잘못된건가 싶어 new Date() 로 다시 확인해봐도 현재 날짜로 잘 나옵니다이건 useEffect 로 출력한 콘솔창 입니다.
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
새글추가시 새로생신게시글 id
더미데이터가 id 1부터 id 5까지 총 5개의 게시물이 존재하는데, 새글을 한번 추가하면 그 새글이 id 0으로 추가가 되고, 다시 한번 더 추가시 새글이 id 1로 추가가 됩니다 ㅠㅠ 그래서 새글 추가시 게시글정렬도 꼬이게 보여요ㅠㅠ 왜 이런 오류가 난거지 궁금합니다. 코드샌드박스 주소 첨부합니다!https://codesandbox.io/s/hungry-bogdan-59h25q
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
작성완료 시 onCreate 함수 호출 못함
App.js에서 onCreate함수를 가지고 오지 못하고 있는데 어디서 오류가 났는지 찾지못하고있어요ㅠㅠ! 도와주세요!코드보여드릴게여 ㅠㅠ오류상황Cannot destructure property 'onCreate' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined. TypeError: Cannot destructure property 'onCreate' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined. at DiaryEdit (http://localhost:3000/static/js/bundle.js:344:5) at renderWithHooks (http://localhost:3000/static/js/bundle.js:26416:22) at mountIndeterminateComponent (http://localhost:3000/static/js/bundle.js:29702:17) at beginWork (http://localhost:3000/static/js/bundle.js:30998:20) at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:16008:18) at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:16052:20) at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:16109:35) at beginWork$1 (http://localhost:3000/static/js/bundle.js:35983:11) at performUnitOfWork (http://localhost:3000/static/js/bundle.js:35230:16) at workLoopSync (http://localhost:3000/static/js/bundle.js:35153:9)App.jsimport React, { useReducer, useRef } from "react"; import "./App.css"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import Home from "./pages/Home"; import New from "./pages/New"; import Edit from "./pages/Edit"; import Diary from "./pages/Diary"; const reducer = (state, action) => { // return state; let newState = []; switch (action.type) { case "INIT": { return action.data; } case "CREATE": { newState = [...action.data, ...state]; break; } case "REMOVE": { newState = state.filter((it) => it.id !== action.targetId); break; } // 모든 부분을 수정해야해서 다 받아옴 case "EDIT": { newState = state.map((it) => it.id === action.targetId ? { ...action.data } : it ); break; } default: return state; } return newState; }; export const DiaryStateContext = React.createContext(); export const DiaryDispatchContext = React.createContext(); const dummyData = [ { id: 1, emotion: 1, content: "일기 1번", date: 1687326683094, }, { id: 2, emotion: 2, content: "일기 2번", date: 1687326683096, }, { id: 3, emotion: 3, content: "일기 3번", date: 1687326683097, }, { id: 4, emotion: 4, content: "일기 4번", date: 1687326683098, }, { id: 5, emotion: 5, content: "일기 5번", date: 1687326683099, }, { id: 6, emotion: 3, content: "일기 6번", date: 1787321680269, }, ]; function App() { const [data, dispatch] = useReducer(reducer, dummyData); // console.log(new Date().getTime()); const dataId = useRef(0); const onCreate = (date, content, emotion) => { dispatch({ type: "CREATE", data: { id: dataId.current, date: new Date(date).getTime(), content, emotion, }, }); dataId.current += 1; }; const onRemove = (targetId) => { dispatch({ type: "REMOVE", targetId }); }; const onEdit = (targetId, date, content, emotion) => { dispatch({ type: "EDIT", data: { id: targetId, date: new Date(date).getTime(), content, emotion, }, }); }; return ( <DiaryStateContext.Provider value={data}> <DiaryDispatchContext.Provider value={{ onCreate, onEdit, onRemove }}> <BrowserRouter> <div className="App"> <Routes> <Route path="/" element={<Home />} /> <Route path="/new" element={<New />} /> <Route path="/edit" element={<Edit />} /> <Route path="/diary/:id" element={<Diary />} /> {/* <Route path="/diary" element={<Diary />} /> */} </Routes> </div> </BrowserRouter> </DiaryDispatchContext.Provider> </DiaryStateContext.Provider> ); } export default App; DiaryEdit.jsimport { useNavigate } from "react-router-dom"; import React, { useState, useRef, useContext } from "react"; import MyHeader from "./MyHeader"; import MyButton from "./MyButton"; import EmotionItem from "./EmotionItem"; import DiaryDispatchContext from "./../App"; // 감정에 대한 데이터 const emotionList = [ { emotion_id: 1, emotion_img: process.env.PUBLIC_URL + `assets/emotion1.png`, emotion_descript: "완전 좋음", }, { emotion_id: 2, emotion_img: process.env.PUBLIC_URL + `assets/emotion2.png`, emotion_descript: "좋음", }, { emotion_id: 3, emotion_img: process.env.PUBLIC_URL + `assets/emotion3.png`, emotion_descript: "보통", }, { emotion_id: 4, emotion_img: process.env.PUBLIC_URL + `assets/emotion4.png`, emotion_descript: "나쁨", }, { emotion_id: 5, emotion_img: process.env.PUBLIC_URL + `assets/emotion5.png`, emotion_descript: "완전 나쁨", }, ]; // 달력에 오늘의 날짜를 기본 날짜로 구현하기 export const getStringDate = (date) => { let year = date.getFullYear(); let month = date.getMonth() + 1; let day = date.getDate(); if (month < 10) { month = `0${month}`; } if (day < 10) { day = `0${day}`; } return `${year}-${month}-${day}`; }; const DiaryEdit = () => { const contentRef = useRef(); const navigator = useNavigate(); const [date, setDate] = useState(getStringDate(new Date())); // 어떤 감정을 선택했는지 state에 저장 const [emotion, setEmotion] = useState(3); const [content, setContent] = useState(""); const { onCreate } = useContext(DiaryDispatchContext); const handleClickEmotion = (emotion) => { setEmotion(emotion); }; const handleSubmit = () => { if (content.length < 1) { contentRef.current.focus(); return; } onCreate(date, content, emotion); }; return ( <div className="DiaryEdit"> <MyHeader headText={"새로운 일기 쓰기"} leftChild={ <MyButton text={"< 뒤로가기"} onClick={() => navigator(-1)} /> } /> <div> <section> <h4>오늘은 언제인가요?</h4> <div className="input_box"> <input className="input_date" value={date} onChange={(e) => setDate(e.target.value)} type="date" /> </div> </section> <section> <h4>오늘의 감정</h4> <div className="input_box emotion_list_wrapper"> {emotionList.map((it) => ( <EmotionItem key={it.emotion_id} {...it} onClick={handleClickEmotion} isSelected={it.emotion_id === emotion} /> ))} </div> </section> <section> <h4>오늘의 일기</h4> <div className="input_box text_wrapper"> <textarea placeholder="오늘은 어땠나요?" ref={contentRef} vlaue={content} onChange={(e) => setContent(e.target.value)} /> </div> </section> <section> <div className="control-box"> <MyButton text={"취소하기"} onClick={() => navigator(-1)} /> <MyButton text={"작성완료"} type={"positive"} onClick={handleSubmit} /> </div> </section> </div> </div> ); }; export default DiaryEdit;
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
제가 생각하는 일기 분석 시작이 2번 되는 호출되는 이유가 맞는지 궁금합니다
const getData = async () => { const res = await fetch( "https://jsonplaceholder.typicode.com/comments" ).then((res) => res.json()); const initData = res.slice(0, 20).map((it) => { return { author: it.email, content: it.body, emotion: Math.floor(Math.random() * 5) + 1, created_date: new Date().getTime(), id: dataId.current++, }; }); setData(initData); }; useEffect(() => { getData(); }, []); const getDiaryAnalysis = useMemo(() => { console.log("일기분석"); | 처음 App 컴포넌트 실행이 되었을 때 useEffect를 통해 getData는 데이터를 불러오고 있는 상태이고(비동기가 맞을까요?) getDiaryAnalysis가 실행되면서 처음 "일기분석" 실행되고getData에서 데이터를 불러 온 상태가 되면 useEffect가 mount되면서 App컴포넌트가 한번 더 실행되는게 맞을까요?
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
props 질문 있습니다.
const OddEvenResult = ({ count }) => { return <div>{count % 2 === 0 ? "짝수" : "홀수"}</div>; }; export default OddEvenResult; const OddEvenResult = (count ) => { return <div>{count % 2 === 0 ? "짝수" : "홀수"}</div>; }; export default OddEvenResult; count를 props로 넘겼을 때 첫번째는 제대로 동작하지만 2번째는 짝수,홀수 구분을 하지 못합니다. 하지만 2번째도 count가인건 인지하고 있습니다(홀수로 출력되기 때문) counter.js에서 initalvaue가 객체이기 떄문에 oddevenresult에서도 {} 이렇게 받아야 제대로 동작 하는걸까요?
- 미해결한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
vscode formatting
vscode에서 저장시 foramting을 어떻게 해야할까요?const EmotionItem = ({ emotion_id, emotion_img, emotion_description, onClick }) 저는 저장을 해도 이렇게 한줄로 계속 남습니다.
- 해결됨한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
비동기를 동기로 처리하는 이유
비동기 함수를 왜 굳이 동기처럼 실행시키기 위해 await을 사용하는 건가요?처음부터 함수를 만들때 동기로 만들면 되는거 아닌가요?동기, 비동기가 잘이해가 안가네요..
- 해결됨한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
함수 동작 원리가 궁금합니다.
function checkmood(mood, goodCallback, bacCallback) { if (mood === "good") { goodCallback(); } else { bacCallback(); } } checkmood("good", sing, cry); function sing() { console.log("sing"); } function cry() { console.log("cry"); } checkmood("good", sing, cry) 이 부분에서 sing이 함수이지만 값으로 들어갔는데, 함수 인자는 값으로만 들어가는걸까요?!
- 해결됨한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
방향성에 대한 강의
마지막 멘트하실 때 앞으로 뭘 더 공부하고 어떻게 공부하면 좋을지 알려주신다고 하셨는데 그것에 대한 강의는 없는 건가요? 완강했습니다! 감사합니다~