Notice
Recent Posts
Recent Comments
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

HYEWON JUNG의 개발일지

20231113 TIL Fan Letter -2 편지추가하기 , 분류버튼, 상세페이지 만들기, router로 연결하기 본문

개발일지

20231113 TIL Fan Letter -2 편지추가하기 , 분류버튼, 상세페이지 만들기, router로 연결하기

혜won 2023. 11. 14. 07:05

목표

  • 추가 버튼 구현
  • 분류 버튼 구현
  • 컴포넌트 분리
  • main - 상세페이지 라우터 구현 
  • 헤더 layout 분리
  • styled component 적용 

새로 알게 된것/ 오늘의 코드

원래는 컴포넌트 분리와 styled component 는 바로 해야하지만 아직 익숙하지 않아서 기본으로 구현 후 변경하려는중이다.

편지 생성(리팩토링 필요)

  const [letters, setLetters] = useState(data);
  
  function IdolLetters({fanLetterStyle, imgStyle,letters}) {
  return (
    <div>
        {letters
        .filter((letter)=>{
            return letter.writedTo === "아이돌"
        })
        .map((letter) => {
        return (
          <div style={fanLetterStyle} key={letter.id}>
            <div>
              <img style={imgStyle} src={letter.avatar}></img>
            </div>
            <div>
              <p>{letter.nickname}</p>
              <p>{letter.createdAt}</p>
              <p>{letter.content}</p>
              
            </div>
          </div>)
      })}
    </div>
  )
}

todolist에서 했듯이 filter에 chaining으로 map을 했다  writeTo의 분류 대로 컴포넌트를 만들었다. 기재한 코드는 아이돌 부분!

 

편지 추가(일시 부분 수정 필요해보임)

//select 부분
const writedToSelectList = ["아이돌", "솔로가수", "배우"]
  const [writedTo, setWritedTo] = useState("아이돌")
  const writedToHanldler = (event) => setWritedTo(event.target.value)

  const [letters, setLetters] = useState(data);
  const [nickname, setNickname] = useState("");
  const [content, setContent] = useState("");
  const [avatar, setAvatar] = useState("")

//input값
  const nicknameHanldler = (event) => setNickname(event.target.value)
  const contentHanldler = (event) => setContent(event.target.value)
  const avartaHanldler = (event) => setAvatar(event.target.value)
//일시
  const date = new Date()
  const years = date.getFullYear();
  const day = date.getDate();
  const month = date.getMonth();
  const hours = date.getHours();
  const minutes = date.getMinutes();
//추가
const addLetter = function (event) {
    event.preventDefault()
    const newLetter = {
      "createdAt": `${years}-${month}-${day} ${hours}:${minutes}`,
      "nickname": nickname,
      "avatar": avatar,
      "content": content,
      "writedTo": writedTo,
      "id": uuid()
    }

    console.log(newLetter)
    setLetters([...letters, newLetter])
    setContent("")
    setNickname("")
    setAvatar("")

  }
  //입력폼
  return
        <form style={formStyle}>
        <div>닉네임 : <input value={nickname} onChange={nicknameHanldler} /></div>
        <div>내용 : <input value={content} onChange={contentHanldler} /></div>
        <div>프로필 사진 : <input value={avatar} onChange={avartaHanldler} /></div>
        <div>
          <label for="entertainer">누구에게 보낼 실 건가요? </label>
          <select id='entertainer' value={writedTo} onChange={writedToHanldler} required>
            {writedToSelectList.map((writedTo) => {
              return (<option key={writedTo} value={writedTo}>{writedTo}</option>)
            })}
          </select>
        </div>
        <button onClick={addLetter}>등록</button>
      </form>

 

select 드롭다운 부분을 map을 돌렸다 

일시를 가져오는 법을 잘못한 것 같긴 한데 잘 모르겠으니.. 우선은 두기

사진이 죽어도 안들어가서 머리 부여잡고 있었는데 

내가 할 수 없는 걸 튜터님들은 알고계신것임. 그리고 심화에서 나온다는 거겠지.

헤더버튼 필터링(리팩토링 필요)

  const [idolLettersShown, setIdolLettersShown] = useState(true)
  const [actorLettersShown, setActorLettersShown] = useState(true)
  const [singerLettersShown, setSingerLettersShown] = useState(true)
  
  return
            <button style={buttonStyle} onClick={() => {
            setIdolLettersShown(true)
            setSingerLettersShown(true)
            setActorLettersShown(true)
          }}>전체</button>
          <button style={buttonStyle} onClick={() => {
            setIdolLettersShown(true)
            setSingerLettersShown(false)
            setActorLettersShown(false)
          }}>아이돌</button>
          <button style={buttonStyle} onClick={() => {
            setIdolLettersShown(false)
            setSingerLettersShown(true)
            setActorLettersShown(false)
          }}>솔로가수</button>
          <button style={buttonStyle} onClick={() => {
            setIdolLettersShown(false)
            setSingerLettersShown(false)
            setActorLettersShown(true)
          }}>배우</button>
		{
          idolLettersShown === true ? <IdolLetters letters={letters} imgStyle={imgStyle} fanLetterStyle={fanLetterStyle} /> : ""
        }
        {
          singerLettersShown === true ? <SingerLetters letters={letters} imgStyle={imgStyle} fanLetterStyle={fanLetterStyle} /> : ""
        }
        {
          actorLettersShown === true ? <ActorLetters letters={letters} imgStyle={imgStyle} fanLetterStyle={fanLetterStyle} /> : ""
        }

 

state로 헤더 버튼 별 Shown 을 만들어주고 초기값을 true로 준다음 버튼별로 set을 해줄 수 있도록 설정을 했고 state별 값이 true인 경우에 각 컴포넌트가 보일 수 있고록 했다.보다 보니 조금 비효율 적인 코드인듯 하다

 

Router 연결하기

Router.js

    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/:id" element={<Detail />} />
      </Routes>
    </BrowserRouter>

Letters.jsx

<li style={fanLetterStyle} key={letter.id} onClick={(item)=>{navigate(`/${item.id}`, {state:letters})}}>

detail에 letters를 내려줘야해서 {state : 내릴 것} 해서 내려주었다. prop내려 주는 것과 같은 원리지만 주고 받는 방식이 다르다.  navigate의 두번 째 인자로 {state : props 내려줄것} 을 넣고 받을 곳에서 const {state}= useLocation() 으로 props를 내려줄 수 있다!

 

detail.jsx

import React from 'react'
import { useLocation } from 'react-router-dom'
import { useParams } from 'react-router-dom'

function Detail() {
    const {state} = useLocation();
    const params = useParams();
    console.log(params)
    const foundLetter =state.find((item)=>{
        return item.id === parseInt(params.id)
    });
    console.log(foundLetter)

  return (
    <div>
        <h3>할 일 :</h3>
    </div>
  )
}

export default Detail

이렇게 해주었는데  letters는 state로 잘 내려왔지만 params가 제대로 내려오지 않았다. 오탈자나 이상한 것을 찾아보았다.

 

하나 찾았다. Letters에서  넘겨주는 params가 잘못되어있었다. 

<li style={fanLetterStyle} key={letter.id} onClick={()=>{navigate(`/${letter.id}`, {state:letters})}}>

이제 params는 나오게 되었다 그래도 foundLetter는 undefined 다.

 

++ 완전 해결..

 

parseInt의 문제였다.  parseInt는 숫자형 지수로는 메소드인데 상황에 맞지 않았다.

참고로 본 예제에서 데이터의 id는 숫자형으로 id: 0 이렇게 되었지만 지금 letter는 id:"0" 으로 문자형 그리고 추가되는 letter도 id:uuid() 로 받기 때문에 문자형으로 들어오게 된다. 

그래서 일치연산자는 데이터타입까지 같아야하기 때문에 계속 나오지 않았던 것이다.

당연하게 id가 숫자형이라고 생각했던 탓에 한참을 오류에 헤맸다. 

    const {state} = useLocation();
    const params = useParams();
    console.log(params)
    const foundLetter =state.find((letter)=>{

        return letter.id === params.id
    });

 

 

 

 

 

현재 컴포넌트 

<App> 자식으로

<Header/>

<Footer/>

<LetterBox/> 자식으로<Letters/>

<Form/>

코드 리팩토링

  • form태그에 button에 type=submit 주고 form에 onsubmit으로 이벤트 주기
  • map을 3개가 아니라 하나로 단축할 수 있을 것 같다. 아예 필요없는 컴포넌트 분리 인듯!
  • 일시를 좀더 효율적으로 나타낼 수 있는 것 찾아보기 // 아니 월 부분만 -1해서 나오고 있었네..
  • 전체부분에서 순서가 최신순이었으면 좋겠다. 지금은 아이돌, 솔로가수, 배우 순으로 분류별 제일 밑으로 가는중!
  •  

목표 달성여부

  • 추가 버튼 구현
  • 컴포넌트 분리 => 리팩토링

총 3개의컴포넌트를 조금 수정해서  props로 넘겨주는 writeTo의 값을 다르게 해서 하나의 컴포넌트에서 filter로 나타낼 수 있도록 했다.

app.jsx

{
          idolLettersShown === true ? <Letters letters={letters} writedTo ={"아이돌"}/> : ""
        }
        {
          singerLettersShown === true ? <Letters letters={letters} writedTo ={"솔로가수"}/> : ""
        }
        {
          actorLettersShown === true ? <Letters letters={letters} writedTo ={"배우"}/> : ""
        }

Letters.jsx

function Letters({letters, writedTo}) {
  return (
    <div>
        {letters
        .filter((L)=>{
          return L.writedTo === writedTo
        })
        .map((letter) => {
        return (
          <div style={fanLetterStyle} key={letter.id}>
            <div>
              <img style={imgStyle} src={letter.avatar}></img>
            </div>
            <div>
              <p>{letter.nickname}</p>
              <p>{letter.createdAt}</p>
              <p>{letter.content}</p>
              <>{letter.writedTo}</>
            </div>
          </div>)
      })}
    </div>
  )
}

export default Letters
  • 분류 버튼 구현 => 리팩토링 //몰것다. allshown을 만들어서 했는데 writeTo를 props로 넘겨줄 때 3개다 주려고 했는데 여러개가 전달이 안됨.. => 리트라이 //성공 똑같이 allShown state를 만들고 writeTo를props로 넘겨줄 때 "아이돌", "배우", "솔로가수" 가 아니라 "아이돌 배우 솔로가수" 이렇게 넘겨주고  Letters 컴포넌트에서 filter 조건을 일치연산자가 아니라 includes로 바꿔서 진행했다.

LetterBox.jsx

{allLettersShown === true ? <Letters letters={letters} writedTo={"아이돌 솔로가수 배우"} /> : ""}

Letters.jsx

.filter((L)=>{
          return writedTo.includes(L.writedTo)
        })
  • main - 상세페이지 라우터 구현 
  • 헤더 layout 분리 => 약간 굳이 싶어서.. 
  • styled component 적용 

화질 왜이래

마무리

여기까진 메소드만 구글링 하고 구현이 가능했다! 스탠다드반에서 배운 것이 많이 도움이 되었다. 삼항연산자를 이용해서 조건부 shown을 주는 것을 몰랐으면 이걸로 또 하루종일 잡고 있었을 것이다.. 

usestate만 이용하면 아직은 살만한데 context로 넘어가면 바로 힘들어지겠지 

내일 drilling 부분 끝내고 context 하자!! 

 

저번 특강에서 번아웃이 오지 않는 법은 일을 미루지 않는 것이라고 해서 정말 목표에 거의 도달할 때까지 자지 않고 했다.

 

내일 목표

  • styled component 적용 
  • detail 수정 삭제 구현
  • 이미지 디폴트 넣기
  • drilling 리팩토링 후 context 넘어가기

'개발일지' 카테고리의 다른 글

20231115 TIL Fan Letter -3 상세페이지 수정 삭제 구현  (0) 2023.11.16
20231114 TIL  (0) 2023.11.15
20231107 TIL  (0) 2023.11.11
20231110 TIL Fan Letter -1  (0) 2023.11.11
20231109 TIL  (0) 2023.11.10