본문 바로가기

프로젝트/멋쟁이사자처럼

팀 프로젝트 - 러브체인

멋쟁이 사저처럼

[NFT 블록체인 마켓 앱 만들기] 2기 클래스

Solidity언어로 스마트 컨트랙트를 짜고

Next.js를 활용하여 사랑의 자물쇠 프로젝트를 진행해 보았다.

프론트엔드 개발팀원으로 참여하여 여러번의 삽질을 했던 기록을 남기고 싶었다.

 

 

[01] Next.js


이번 프로젝트 팀장님께서 현업 프론트엔드 개발자로 일한지 2년정도 되시는 경력자 분이셔서

요즘 많이 쓰인다는 리액트의 라이브러리인  Next.js 로 개발을 진행하기로 했다.

 

 Next.js 는 불필요한 설정을 하지 않고도 SSR, SEO부터 TypeScript까지

생산에 필요한 많은 기능들을 제공하는 아주 강력한 React 프레임워크다.

 

SSR(Server Side Rendering)
SSR을 사용하면 모든 데이터가 맵핑된 서비스 페이지를
Client(브라우저)에게 바로 보여줄 수 있다.

서버를 이용해서 페이지를 구성하기 때문에 클라이언트에서 구성하는 CSR(client-side rendering)보다 페이지를 구성하는 속도는 늦어지지만 전체적으로 사용자에게 보여주는 콘텐츠 구성이 완료되는 시점은 빨라진다는 장점이 있다.

 

SEO(Serch Engine Optimization)

 

검색엔진 최적화를 하기 위해서 SSR이 중요한 역할을 한다.

검색엔진 봇들은 JavaScript를 해석하기 힘들기 때문에 HTML에서 크롤링하게 된다.

CSR 방식은 Client 측에서 페이지를 구성하기 전에 HTML에 아무것도 없으므로

데이터를 수집하지 못해 검색엔진에 노출이 어렵다.

 

[02] 객체배열 상태관리 (feat. useState)


const handleOptionClick = (e) => {
        const { name, value } = e.target
        const reverseValue = (value == 'false')
        
        // 버튼 클릭했을 경우 true/false 서로 반대로 바꾸기
        setOptionData({
            ...optionData,
            [name]: reverseValue 
            // [name]은 name에 어떤 변수가 올지 모르기 때문에
            // 기존의 optionData를 spread한 뒤에 새로운 변수로 교체한다
        })

        // 내림차순 버튼을 클릭했을 경우 오름차순 버튼 비활성화
        if (name == 'desc') {
            setOptionData({
                ...optionData,
                price: {
                    order: 'desc',
                    asc: false,
                    desc: reverseValue
                }
            })
        }

        // 오름차순 버튼을 클릭했을 경우 내림차순 버튼 비활성화
        if (name == 'asc') {
            setOptionData({
                ...optionData,
                price: {
                    order: 'asc',
                    asc: reverseValue,
                    desc: false
                }
            })
        }
    }
    
    retrun (
    	<>
            <ButtonGroup>
                <Button 
                    name='desc'
                    value={optionData.price.desc}
                    onClick={handleOptionClick}
                >
                    가격 내림차순
                </Button>
                <Button 
                    name='asc'
                    value={optionData.price.asc} 
                    onClick={handleOptionClick}
                >
                    가격 오름차순
                </Button>
            </ButtonGroup>
        </>
    )

 

[03] 내림차순/오름차순 정렬


// 내림차순 & 오름차순 정렬
    if (optionData.order == 'desc') {
        lockList.sort((a, b) => {
            return b - a
        })
    } else {
        lockList.sort((a, b) => {
            return a - b
        })
    }

 

[04] url주소로 서버 데이터 필터링


쿠팡같은 쇼핑 서비스에서 내가 원하는 옵션으로만 검색해서필터링한 결과 데이터를 보여주게 되는데,

러브체인에서도 마찬가지로원하는 옵션을 선택했을 때 해당하는 데이터만 서버에 요청하여 불러오도록 구현하였다.axios get에 쓰이는 url 주소만 바꿔서 요청을 보내면 해결가능하다

 

const [optionData, setOptionData] = useState({
        price: {
          order : 'desc',
          asc : false,
          desc : false
        },
        coupleImage: false,
        date: false,
        oneLine: false,
        socialProfile: false,
        isAvailable: false,
        offset: 0 + (12 * page)
    })

  // optionData에 따른 url 변경 적용
  const url = `
    https://hohyeon.site/api/item?
    ${optionData.date ? 'date=true' : ''}
    ${optionData.oneLine ? '&oneLine=true' : ''}
    ${optionData.coupleImage ? '&coupleImage=true' : ''}
    ${optionData.socialProfile ? '&socialProfile=true' : ''}
    ${optionData.isAvailable ? '&isAvailable=true' : ''}
    ${optionData.price.order == 'desc' ? '&price=desc' : '&price=asc'}
    &limit=12
    &offset=${offsetNumber}
  `;
  
  // limit : 불러올 데이터의 개수
  // offset : 불러올 데이터의 시작 인덱스 번호

 

[05] 무한 스크롤링 기능 구현


모든 데이터를 한번에 보여주려고하면 화면에 로딩되는 시간이 길어질 수밖에 없다.

사용자가 스크롤하면서 내릴때마다 데이터를 반복적으로 누적해서 불러오는 방법이 무한스크롤링이다.

react-intersection-observer 라이브러리에 있는 useInView를 사용하면 편리하게 구현할 수 있다.

 

맵핑된 데이터의 마지막 Element에 ref를 심어두기만 하면 된다.

스크롤을하다가 ref가 심어진 Element에 도달하는 즉시 inView는 "true"로 세팅된다.

그러면 page가 "1"증가하게 되고, getMoreItems함수를 다시 실행하게 되는데

page에 따라 서버에서 불러올 데이터의 시작점을 바꿔서 url 요청을 보내게 된다.

받아온 데이터는 [...prevState.list, ...data.list]에 의해 기존 데이터에 누적되어서 추가로 보여지게 된다.

 

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { useInView } from "react-intersection-observer"

function LockCard() {
    const [ref, inView] = useInView()
    const [page, setPage] = useState(0)
  	const [scrolling, setScrolling] = useState(false)
    const [lockData, setLockData] = useState({
        list : [],
        total: 0
    })
    
    // 무한 스크롤링 기능
    const getMoreItems = async() => {
        setScrolling(true)
        try {
            const { data } = await axios.get(url)
            setLockData(prevState => ({
                list: [...prevState.list, ...data.list], //기존 데이터에 추가 데이터 누적하는법
                total: data.total
            }))
        } catch(err) {
            console.log(err)
        }
        setScrolling(false)
    }
    
    // page변할때마다 함수 불러오기
    useEffect(() => {
        getMoreItems()
    }, [page])
    
    // 무한스크롤링 실행
    useEffect(() => {
        if (inView && !scrolling) {
            setPage(prevState => prevState + 1)
        }
        console.log('page : ', page)
    }, [inView])
    
    return (
        <div>
            {lockList.map((item, idx) => {
                const lastElement = lockList.length - 1
                return (
                    <div key={idx}>
                        <div ref={(idx == lastElement ? ref : null)}>
                            <Image src={item.lockImage} alt={'lockImage'} />
                        </div>
                    </div>
                )
            })}
        </div>
	);

 

참고 : https://slog.website/post/8

[06] 프로젝트 시연 영상