본문 바로가기
프론트엔드/React

[React/js/scss] MBTI 테스트 만들기 코드 리뷰 (나와 잘 맞는 IT 직군/유명인 테스트)

by JI NY 2024. 7. 17.

MBTI 테스트 만들기 코드 리뷰 (나와 잘 맞는 IT 직군/유명인 테스트)

react / scss / javascript (JS)

결과물

 

📖 요약

  • '울청스타'는 다양한 학과를 체험하고 진로를 탐색할 수 있는 특별한 행사입니다. 가온누리 학술 동아리에서 IT 분야에 대한 흥미를 불러일으키고자 '컴퓨터야 놀자!'라는 주제로 부스를 운영했습니다.
  • MBTI를 통해 나와 찰떡궁합인 IT 직군과 유명인을 찾아볼 수 있는 체험으로 준비했습니다.
  • 배포 링크 : https://ji-ny.github.io/mbti_test_ulsan/
 

울청스타 IT직업 & 유명인 MBTI 테스트

울청스타 IT직업 & 유명인 MBTI 테스트 해보기! 나의 유형은?

ji-ny.github.io

 

※ 참고 : 이미지 파일 출처는 나무위키, 뉴스 등 구글에 나와있는 이미지로 사용했습니다. 저작권 관련으로 문제가 있는 이미지가 있으면 말씀해 주시면 바로 변경하겠습니다. (해당 mbti 테스트 이미지를 통해 따로 상업적 이익을 취하지 않습니다)



⭐  사용 기술 및 라이브러리

  • React, JavaScript, scss
  • OpenGraph, react-helmet-async
  • github pages
  • react-router-dom

  주요 기능 

  • 공유하기 / 미리보기 화면
  • github.io 배포
  • mbti 테스트 진행 시작화면 / 테스트화면 / 결과화면
  • 반응형 웹

  제작 기간

  • 2024.03.07 ~ 2024.05.17 : 약 2개월


🎄 폴더구조

 


 

😎 주요 코드 리뷰

컴포넌트의 경우 주로 다음과 같이 이루어져 있습니다.

  • Home컴포넌트 
    • Start (시작화면)
    • Question (질문)
    • Result (결과화면) 

 

01. App.js

function App() {

  return (
    <div>
      <SetMetaTag title="cute" description="설명부분" image={mainImage}/>
      <Routes>
        <Route path={'/mbti_test_ulsan'} element={<Home/>}/>
        {/* <Route path={'/mbti_test_ulsan/result'} element={<div>결과 페이지에용 .</div>}/> */}
      </Routes>
      
    </div>
  );
}

export default App;

 

App 화면의 경우, Route를 이용해 path를 mbti_test_ulsan 를 메인페이지로 설정하여 만들어줬습니다.

(원래는 app.js에 다 때려넣으려고 했는데 이건 아닌 것 같아서 분리를 해줬습니다.)

 

 

1)  SetMetaTag.js : 메타태그 설정해주기

react-helmet-async 라이브러리를 사용하여 메타태그를 설정해 줬습니다.

처음 목표는 각 페이지별로 메타태그를 다르게 하려고 했으나,, 실패했습니다.

그래서 그냥 전체 다 같은 메타태그를 사용하도록 했습니다.

 

사실 이 경우에는 helmet 라이브러리를 쓰지 않고, 바로 index.html에서 openGrpah 태그를 넣어줘도 될 것 같습니다.

 

1. index.js 페이지에서 <App/>을 HelmetProvider로 감싸줍니다.

Helmet 컴포넌트는 HelmetProvider 컴포넌트 안에 있어야 하기 때문입니다.

 

2. setMetaTag.js

<Helmet>으로 감싸주고, openGraph 문법으로 제목, 설명, img, url 메타태그를 설정해 줬습니다.

const SetMetaTag = props =>{
    // props로 content 내용을 불러온다.
    return(
        <Helmet>
            <meta
                property="og:title" content={"울청스타 IT직업 & 유명인 MBTI 테스트"}
            />
            <meta
                property="og:description"
                content={"울청스타 IT직업 & 유명인 MBTI 테스트 해보기! 나의 유형은?"}
            />
            <meta 
                property="og:image" content={props.image} 
                />
            <meta property="og:url" content="https://Ji-ny.github.io/mbti_test_ulsan"/>
        </Helmet>
    );
};


export default SetMetaTag;

 

 

🌠 아래와 같이 결과적으로 공유했을 때 미리 보기가 설정됩니다!

울청스타 IT직업 & 유명인 MBTI 테스트 (index.html)

 

울청스타 IT직업 & 유명인 MBTI 테스트

울청스타 IT직업 & 유명인 MBTI 테스트 해보기! 나의 유형은?

ji-ny.github.io

 

 

 


 

02. Home.js

1. home화면의 경우 제일 처음 뷰포트의 높이를 구해 vh 사이즈를 적절히 구할 수 있도록 했습니다.

  // ** =============뷰포트 높이 구하기 ===============** //
  
  /** 현재 뷰포트 높이의 1%를 계산한다. */
  const setVh = () => {
    const vh = window.innerHeight * 0.01; //window.innerHeight : 뷰포트의 높이를 가져온다. | 뷰포트 높이를 100분의 1로 나누어서 사용하는 것 (1%)
    document.documentElement.style.setProperty('--vh', `${vh}px`); // css 변수를 만들어준다.
  }

  // * 첫 렌더링시, 뷰포트 사이즈 계산 후 적용
  useEffect(()=> {
    setVh();
    
    // 사이즈가 변경될 때, 다시 뷰포트 높이를 구한다.
    function onResize(){
      setVh();
    }
                            // 이벤트 유형 , 이벤트가 발생할때 실행하는 함수.
    window.addEventListener('resize', onResize);
  },[]);


  // ** ============================================** //

 

2. 각 페이지 관리

현재 페이지번호를 토대로 시작/질문/결과 페이지에 맞게 나누기 위해 page state를 선언해 줬습니다.

  const [page, setPage] = useState(0); // 페이지 번호 상태

 

 

 

3. page 번호에 따른 컴포넌트 나타내기

  return (
    <div className="mbtiLayout">
      {/* 페이지에 따라 나타내는 상태가 다르게 한다. */}
      {page===0?
        <Start page={page} setPage={setPage}/>
        
        // !현재 페이지가 질문 길이보다 작다면,
        : page <= questionList.length? 
        <div className='questionLayout'>
          {/* 프로그레스바 */}
          <ProgressBar page={page} questionList={questionList}/>

          {/* 질문 부분 */}
          <Question questionList = {questionList} page={page} mbtiList={mbtiList} handleCkAnswer={handleCkAnswer}/>
        </div>
        : // !현재 페이지가 질문 길이보다 크다면, (결과페이지)
          <Result mbtiContents={mbtiContents} mbtis={mbtis} />
      }
    </div>
  );
  • 0 : 시작페이지
  • 현재 페이지가 질문 길이보다 작은 경우 : 질문 페이지
  • 현재 페이지가 질문 길이보다 긴 경우 : 결과페이지

 

4. mbti 결과를 저장할 state를 mbtiContents라는 변수로 제작해 줬습니다.

const [mbtiContents, setMbtiContents] = useState([]);

 

 

5. mbti 변수 : mbti 내용들 선언

mbti의 내용들 또한 object array (객체 배열) 방식으로 총 16가지를 만들어줬습니다.

안에는 mbti이름, 관련 IT 기업인 또는 직무, 특징들, 잘 맞는 유형, 맞춰가야 하는 유형, image가 있습니다.

  const mbtis = [
    {   mbti:"ISTP", 
        name: ['스티브잡스 (APPLE CEO)'], 
        contents:['벼락치기 진짜 잘해요.','논리적이고 뛰어난 적응력을 가지고 있어요.','처음엔 낯을 가리지만 친해지면 장난도 잘 쳐요.'],   
        goods:['ESFJ', 'ENTJ'], //ENFP, ENTJ
        bads:['INFP, ENFP, INFJ, ENFJ'],
        image:istpImage,
      },    // 백과사전형
        
        

    {   mbti:"ISFP", 
        name: ['이재용 (삼성전자 회장)'], 
        contents:['타인 배려를 잘하지만 눈치를 많이 봐요.','순하고 정이 많아요.','상대방의 이야기를 잘 들어줘요.'],   
        goods:['ENFJ','ESFJ','ESTJ'], //INFJ, INTJ
        bads:['INFP', 'ENFP', 'INFJ'],
        image:isfpImage,
      }, //default    // 성인군자형
      
      
      ....

 

 

6. setMbti()를 이용해 맞춤 mbti를 설정해 주는 함수를 선언해 줬습니다.

  • I/E, N/S, F/T, J/P인지 4가지 유형을 각각 찾은 뒤, 
  • 찾은 MBTI를 모두 합쳐주어 하나의 string으로 만들어주고 (mbti 변수로)
  • mbti.filter를 이용해 mbtis의 16가지 유형중, 결과로 나온 mbti와 같은 것을 걸러내어, 
  • setMBTIContents를 통해 결과 mbti 유형 객체를 담을 state를 업데이트해 줍니다.
  // mbti 설정해주는 함수
  function setMbti(){
    


    // I와 E를 구분한다.
    let IorE= // data : mbtiList를 데표하는 변수.
      mbtiList.find(function(data){return data.name === "I"}).count >
      mbtiList.find(function(data){return data.name === "E"}).count ? "I" : "E";

    //구분한다.
    let NorS= // data : mbtiList를 데표하는 변수.
      mbtiList.find(function(data){return data.name === "N"}).count >
      mbtiList.find(function(data){return data.name === "S"}).count ? "N" : "S";

          //구분한다.
    let ForT= // data : mbtiList를 데표하는 변수.
      mbtiList.find(function(data){return data.name === "F"}).count >
      mbtiList.find(function(data){return data.name === "T"}).count ? "F" : "T";

                //구분한다.
    let JorP= // data : mbtiList를 데표하는 변수.
    mbtiList.find(function(data){return data.name === "J"}).count >
    mbtiList.find(function(data){return data.name === "P"}).count ? "J" : "P";
    
  
    // 아래 변수에, 찾은 MBTI를 모두 합쳐준다.
    let mbti = IorE+ NorS + ForT + JorP;
    console.log("결과:mbti",mbti);

    //
    setMbtiContents(mbtis.filter((val)=>val.mbti === mbti)[0]); 
    
  
  }

 

7. 결과를 count 해서 저장해 놓은 변수입니다.

  const [mbtiList, setMbtiList ] = useState([
    {name:"I", count:0}, {name:"E", count:0}, {name:"S", count:0}, {name:"N", count:0},
    {name:"F", count:0}, {name:"T", count:0}, {name:"P", count:0}, {name:"J", count:0},
  ]);

 


 

1) Start.js 컴포넌트 : mbti 시작화면 

1. 시작화면의 경우, 단순하게 메인 페이지를 나타냅니다.

function Start({page, setPage}){

    return(
        <div className='startPageLayout'>
            <div className='startLogo'>
                <div>울청스타</div>
                <div>나와 잘 맞는 IT직군</div>
                <div>& 유명인 테스트</div>
                <img src={mainImage} alt="사진"/>
            </div>
            <div onClick={()=> setPage(1)} className='startButton' >테스트 시작하기</div>
            <div className='startIntro'> 현재까지<span style={{color:"#59A5F5", fontWeight:"bold"}}> {number} </span>명의 <br></br>친구가 참여했어요😊</div>
        </div>
    )
}

export default Start;

 

1.1 특히 Start 컴포넌트는 (setPage)를 인자로 받기 때문에 '테스트 시작하기' 버튼 클릭 시, Page를 1로 설정하여,  질문 페이지로 넘어갈 수 있게 합니다.

  <div onClick={()=> setPage(1)} className='startButton' >테스트 시작하기</div>

 

🌠 시작화면 결과 ( GIF)

 

 


2) Question.js  : mbti 테스트 질문 페이지

1. 질문 페이지의 경우, 다음과 같은 props를 가집니다.

function Question({questionList, page, mbtiList, handleCkAnswer}){

 

  • questionList: 질문과 답변의 목록을 포함하는 배열.
  • page: 현재 페이지 번호.
  • mbtiList: (사용되지 않지만 주석으로 남겨진 부분에서 언급됨) MBTI와 관련된 정보 배열.
  • handleCkAnswer: 사용자가 답변을 클릭했을 때 호출되는 함수

 

 

 

2. 전체 구조

{questionList.map((val, idx) => ( // 질문과 답변을 포함하는 div ))}
  • questionList 배열을 map 함수를 사용하여 순회합니다. 각 질문(val)과 그 인덱스(idx)를 통해 렌더링 합니다.
  • idx를 키로 사용하여 각 질문 요소에 고유한 키를 부여합니다.
  • 각 질문은 조건부로 렌더링 되며, 현재 페이지와 일치하는 질문만 표시됩니다.

3. 질문 나타내기

<div className='questionList' key={idx} style={{display:page===idx+1? "flex" : "none"}}>
    {/* {console.log(mbtiList)} */}
    <div className='questionItemLayout'>
        {/* 내용 */}
        <div className="chatListLayout">
        {val.q.map((qval, qidx)=> // val은 현재 질문 
            <div key={qidx} className='chatBox'> 

            <div>Q{page}.</div> {/* 질문 번호 */}
            <div> {qval} </div> {/* 질문 내용 */}
            </div>
        )}
        </div>
    </div>

 

  • pageidx + 1을 비교하여 현재 페이지와 일치하는 질문만 display: flex로 설정하여 표시합니다. 그렇지 않은 경우 display: none으로 숨깁니다.
  • 각 질문(val.q)에 대해 map을 사용하여 질문 내용을 렌더링 합니다.
  •  div 태그를 활용해 질문 번호와 그에 맞는 질문 내용을 렌더링 했습니다.

 

4. 답변 나타내기

    <div className='answerItemLayout'>

        {/* 답변 내용 */}
        {val.a.map((aval,aidx) => 
        <div key={aidx} className="answerBox" onClick={() => handleCkAnswer(aval.type, idx )}>
            {aval.text}
        </div>
        )}
    </div>
</div>

 

  • val.a (현재 질문의 답변 부분(answer) 배열을 map을 사용하여 순회하면서 각 답변을 나타냈습니다.
  • 각 답변(aval)은 고유한 키(aidx)를 가지며, onClick으로 클릭 이벤트는 handleCkAnswer 함수를 호출합니다
    • 선택된 답변의 타입(aval.type) ( P인지, J인지,, 등)과 현재 질문의 인덱스(idx)를 인자로 전달합니다.

 

※ 참고로, qustionList의 각 요소로 val이 이루어져 있으며, 아래와 같습니다!

   // Home.js
     const questionList = [
    {
      q: ['방학이 끝나고, 드디어 새학기가 되었다! 나의 기분은?'],
      a:[ // 답변
        {type:"I", text:"설레면서도 잘 지낼 수 있을까 걱정된다."},
        {type:"E", text:"새로운 친구들을 만날 생각에 너무 기대된다."}]
    },
    
   {
      q: ['모르는 문제가 생겼다. 어디서 알아보지?'],
      a:[ // 답변
        {type:"P", text:"ChatGPT(생성형 AI)에게 물어보고 해결한다"},
        {type:"J", text:"미리 예습해서 모르는게 없다."}]
    },
    ...
    ]

 

 

 

 

 

5. 그러면 Home.js의 handleCkAnswer이 실행되어, 값을 저장하고 페이지를 1 올려줍니다! 

=> 이제 문제로 넘어가고, mbtilist에 알맞은 mbti의 유형의 count를 증가시켜, 결과 저장 업데이트!

 // Home.js
 
  // ** ==== 답변을 클릭했을 때 실행할 함수. (MTTI 타입 / 질문 idx) =====** //
  const handleCkAnswer = (type, idx) => {
    let ls = mbtiList;
    for(let i = 0; i < ls.length; i++){
      if (ls[i].name === type){ //mbtiList의 name이, type과 같을 경우
        ls[i].count +=1; // count값을 1 늘려준다.
      }
    }

    setMbtiList(ls); //mbtiList를 업데이트 시킨다.
    setPage(page+1); // 페이지도 1 업데이트 시킨다.

    //결과페이지 | idx가 질문리스트 길이만큼 됐다면
    if(idx+1 === questionList.length){
      // console.log("결과보기")
      setMbti();
      
    }
  }

  // ** ===========================================** //

 

 

 

2-2) ProgressBar.js : 프로그레스 바 

프로그레스 바의 경우 현재 페이지의 수에 파란색이 점점 차오르게 되는 컴포넌트입니다. 

1. 사진

 

 

2. 코드

- MBTI 제목과, 현재 페이지 수, 페이지에 맞게 차오르는 바 모양을 조정해 줬습니다.

function ProgressBar({page, questionList}){
    return(
        <div className='mbtiTitle'> 
            <div>울청스타 MBTI 테스트</div>  
            {/* 프로그래스 바 */}
            <div className='progress_bar'> 
            {/*  현재 페이지 (1/13%) */}
            <span style={{width:`${(page / 13) * 100}%`}} className='progress_bar_inner'> </span>
            </div>
            <div>{`${page} / ${questionList.length}`}</div> 
        
        </div>
    )
}

export default ProgressBar;

 

3. 주요 로직 (차오르는 바)

- div안에 span을 넣어줬고, span의 너비를 현재 페이지 / 13 * 100%로 하여, 페이지 수에 따라 page에 맞게 올라갈 수 있게 했습니다. 말로 설명하려니 어렵네요 🥹

            <div className='progress_bar'> 
            {/*  현재 페이지 (1/13%) */}
            	<span style={{width:`${(page / 13) * 100}%`}} className='progress_bar_inner'> </span>
            </div>

 

 

🌠 테스트화면 결과

 

 

 

 


 

3). 결과화면

1. 결과화면은 다음과 같은 props를 가집니다.

function Result({mbtiContents, mbtis}){ }
  • mbtiContents: 사용자의 MBTI 결과와 관련된 내용을 포함하는 객체.
  • mbtis: 모든 MBTI 유형과 그에 대응하는 설명을 포함하는 배열.

 

2. 결과 mbti 나타내기

아래 코드를 활용하여, mbtiContests( 결과)의 이름, mbti유형, 이미지, 설명을 나타냅니다.

설명은 객체 배열로 3가지 이상이 있었기 때문에 map을 활용해서 나타냈습니다

<div className='result_title_Layout'>
    <div>나의 IT 유명인 및 직군 유형은?</div>
    <div>{mbtiContents.name}</div> 
    <div>{mbtiContents.mbti}</div>
    <img src={mbtiContents.image} alt="사진"/>
</div>

<div className='result_example'>
    <div>{mbtiContents.mbti}는요,</div>
    {mbtiContents.contents.map((val,idx)=> 
            <div key={idx}>
                <div>{val ? val : "어떤어떤 유형이에요."}</div>  {/* 유형 설명 있다면 띄우기 */}
            </div>
    )}

</div>

 

 

3. 잘 맞아요 & 함께 맞춰가요 부분 

잘 맞는 mbti, 함께 맞춰가는 mbti의 내용을 map으로 매핑하여 각각 나타내줬습니다.

만약에 good, bad에 해당하는 mbti가 없다면 CUTE로 나오게 했습니다.

        <div className="result_mbtis">
            <div className="mbti_item">
                <div>잘 맞아요😊</div>
                {/* 좋은 mbti 모음  // ! find 수정하기 */}
                {mbtiContents.goods.map((val,idx)=> // ISTP,, 등 차례대로 꺼내진다.
                <div className="mbtis" key={idx}>  
                    <li className='mbtis-li'>
                    {mbtis.find(item => item.mbti === val)?.name || "CUTE"}
                    </li>
                </div>
            )}
                
            </div>

            <div className="mbti_item">
                    <div>함께 맞춰가요🥹</div>
                    {/* 별로인 mbti 모음 */}
                    {mbtiContents.bads.map((val,idx)=>
                    <div className="mbtis" key={idx}>
                        <li className='mbtis-li'>
                        {mbtis.find(item => item.mbti === val)?.name || "CUTE"}{/* MBTI 내용 없다면 띄우기 */}
                        </li>
                    </div>
                    )}
            </div>
        </div>

 

4. 다시 하기

다시 하기 버튼을 클릭 시, 새로고침이 되도록 하여 시작화면으로 돌아가게 했습니다.

state로 처음 실행 시 page = 0으로 저장했기 때문에, 새로고침 하면 초기화됩니다.

 <div onClick={()=> window.location.reload()}>

 

5. 공유하기

공유하기 버튼 클릭 시, handleShare 함수를 실행하도록 했습니다.

 <div onClick={handleShare}>
    const handleShare  = async () => { // 비동기를 동기처럼 사용하자.
        // 공유하기에 띄울 데이터 
        const shareData = {
        title: "MbtiTest", // 제목
        text: "울청스타 IT 직군 & 유명인 mbti test 결과보기!", // 공유할때 같이 가는 내용
        url: "https://ji-ny.github.io/mbti_test_ulsan", // 사이트
        };

        // 공유기능!
        const url = window.location.href; // 현재 ㅇurl 정보!
        try{
        await navigator.share(shareData);
        console.log("공유하기 성공");
        } catch{
        // console.log("공유하기 실패");
        await navigator.clipboard.writeText(url);
        alert("클립보드에 링크가 복사되었습니다.");
        }
    }

 

5-1. 공유하기 코드의 ( handleShare  )상세 설명입니다.

 

-  shareData의 경우, 공유했을 때 나타낼 데이터입니다. text가 공유할 때 함께 가는 내용입니다.

        const shareData = {
        title: "MbtiTest", // 제목
        text: "울청스타 IT 직군 & 유명인 mbti test 결과보기!", // 공유할때 같이 가는 내용
        url: "https://ji-ny.github.io/mbti_test_ulsan", // 사이트
        };

 

- window.location.href를 이용하여 현재 url 정보를 저장했습니다.

        const url = window.location.href; // 현재 ㅇurl 정보!

- navigator.share를 사용하여 공유 기능을 구현했고,

        await navigator.clipboard.writeText(url);
        alert("클립보드에 링크가 복사되었습니다.");

share적용이 안 되는 경우는 url을 클립보드에 저장하도록 했습니다.

        await navigator.clipboard.writeText(url);
        alert("클립보드에 링크가 복사되었습니다.");

 

 

🌠 실제로 카카오톡으로 공유하면 다음과 같이 나오게 됩니다!

🌠 결과화면입니다!

 

 

 

 


 

 

 

 

✈️  전체 나의 IT 유명인 및 직군 유형 MBTI 테스트결과 동영상

 


 

💝 깃허브 주소

https://github.com/Ji-ny/mbti_test_ulsan

 

GitHub - Ji-ny/mbti_test_ulsan: [2024 울청스타 축제] IT직군&유명인 MBTI 테스트 웹사이트입니다.

[2024 울청스타 축제] IT직군&유명인 MBTI 테스트 웹사이트입니다. Contribute to Ji-ny/mbti_test_ulsan development by creating an account on GitHub.

github.com


 

💫 후기

 이렇게 제대로 코드 리뷰를 해본 건 처음인데, 비교적 타 프로젝트에 비해 간단한 코드구조 가지고 있음에도 내용이 너무  많아서 쉽지 않았습니다. 

 한번 해보니, 그때 막 썼던 코드나 이해가 잘 안 되어도 넘어갔던 코드들을 다시 되짚어 볼 수 있어서 좋았습니다.

해당 포스팅을 보고 MBTI 만들기에 도전하시는 분들께 도움이 됐으면 좋겠습니다 ㅎㅎ

 그리고 이번에 공유하는 페이지마다 다른 메타태그, 이미지, 등으로 나오게 하려면 Next.js를 사용하는 된다는것도 알게됐습니다. 다음번엔 Next.js를 도전하는걸로..!

 

🔥 추가로,

- 기타 scss 등과 같은 스타일링 코드와, 개발 과정을 함께 보시려면,  블로그에 MBTI 테스트 만들기 과정도 올려뒀으니 참고하시면 좋을 것 같습니다 :)

[React/JS MBTI 만들기] #05. MBTI 이미지 삽입 및 UI, 컨텐츠 수정 (scss, li 태그 커스텀, ::before, ::after, git 배포) (tistory.com)

 

[React/JS MBTI 만들기] #05. MBTI 이미지 삽입 및 UI, 컨텐츠 수정 (scss, li 태그 커스텀, ::before, ::after, git

[React/JS MBTI 만들기] #05. MBTI 이미지 삽입 및 UI, 컨텐츠 수정 (scss, li 태그 커스텀, ::before, ::after) 1. 기간 24.04.01 ~ 24.04.07 : 개발 24.03.29 ~ 20.04.01 : TypeScript 문법 공부 2. 목차 MBTI 컨텐츠 수정 잘 맞는 &

raon-2.tistory.com

 

[React/JS MBTI 만들기] #4. 공유하기, 애니메이션 및 퍼블리싱 (Web Share API / @keyframes / animation / scss) (tistory.com)

 

[React/JS MBTI 만들기] #4. 공유하기, 애니메이션 및 퍼블리싱 (Web Share API / @keyframes / animation / scss)

[React MBTI 만들기] (JavaScript/scss) #04. 공유하기, 애니메이션 구현 및 퍼블리싱 Web Share API / @keyframes / animation / css3 / scss 목차. 00. Intro 01. 변경된 디자인 퍼블리싱 02. 공유하기 구현 03. 애니메이션 구

raon-2.tistory.com

 

[React MBTI 만들기] #03. 결과페이지 제작 / Figma 디자인 / 테스트 컨셉 변경 (tistory.com)

 

[React MBTI 만들기] #03. 결과페이지 제작 / Figma 디자인 / 테스트 컨셉 변경

[React MBTI 만들기] (scss/JavaScript/git) #03. 결과페이지 제작 / Figma 디자인 / 테스트 컨셉 변경 목차.00. Intro 01. 결과페이지 완성. 02. MBTI Figma 디자인 및 컨셉변경 03. 마무리 04. 최종 코드 및 깃허브 00. In

raon-2.tistory.com

[React MBTI 만들기] #02. 시작/테스트/결과 페이지 만들기 | Objects are not valid as a React child 오류 해결 | (scss/JavaScript) (tistory.com)

 

[React MBTI 만들기] #02. 시작/테스트/결과 페이지 만들기 | Objects are not valid as a React child 오류 해결 |

[React MBTI 만들기] (scss/JavaScript/git) #02. 시작/테스트/결과 페이지 만들기 Objects are not valid as a React child 오류 해결 1편에 이어서 이번에는 MBTI 시작페이지와 테스트페이지를 만들었다. 마지막 결과

raon-2.tistory.com

 

[React로 MBTI 테스트 만들기] #01. 리액트 프로젝트를 깃허브로 웹사이트 배포 (gh-page / git / JavaScript) (tistory.com)

 

[React로 MBTI 테스트 만들기] #01. 리액트 프로젝트를 깃허브로 웹사이트 배포 (gh-page / git / JavaScript)

[React로 MBTI 테스트 사이트 만들기] #01. 리액트 프로젝트를 깃허브로 웹사이트 배포 00. Intro 1. 목표 - 이 프로젝트는 리액트(React)를 활용하여 MBTI 테스트 사이트를 만들고, 웹페이지를 깃허브를 이

raon-2.tistory.com

 


참고자료

더보기

댓글