33일차 - Optimistic, 메타 태그

류연찬·2023년 5월 10일
0

Codecamp FE07

목록 보기
33/39

Optimistic-UI(옵티미스틱 UI)

우리가 한 게시물을 보고 좋아요를 누르게 되면 환경에 따라 좋아요의 수가 올라가는 속도가 다릅니다.

왜 그런지는 아래의 그림을 통해 자세히 알아보도록 하겠습니다.

우리가 좋아요를 누르게 되면 백엔드에 likeBoard라는 api에 요청을 보내게 되고, 백엔드는 DB에 요청을 하게 됩니다.

그럼 DB는 좋아요의 수를 올려두고 올린 좋아요 수를 응답합니다. 해당응답을 백엔드는 다시 브라우저에 응답해주는 것 입니다.

그런데 느린환경의 컴퓨터라면, 혹은 백엔드 컴퓨터가 굉장히 먼 곳에 있다면 해당과정이 굉장히 지연될 수 있습니다.

이렇게 되면 유저에게 해당경험은 좋은 경험이 아니겠죠? 따라서 우리는 옵티미스틱 UI를 사용하게됩니다.

옵티미스틱 UI란 요청이 서버에 도달하기도 전에 화면의 값을 바꿔버리는 것 입니다.

즉, likeBoard를 요청하기도 전에 화면에 13으로 바꿔버리고 계속해서 요청을 보내는 것 입니다.

그리고 요청이 성공하고 나면 13이 응답으로 돌아올텐데 그때 다시 화면을 업데이트합니다.

하지만 이미 옵티미스틱 UI로 13을 그려줬기때문에 유저가 보기에는 똑같습니다.

만일 중간에 네트워크 문제나 다른이유로 실패하게 된다면, 이전의 값을 응답으로 보내주고 이전의 값을 화면에 업데이트 해줍니다.

💡 옵티미스틱 UI를 사용해야 할 곳과 사용하지 말아야 할 곳
옵티미스틱 UI는 보통 실패 확률이 낮고 틀려도 괜찮은 중요하지 않은 데이터를 보여주실때 사용합니다.

데이터가 굉장히 중요하고 안정성이 필요할때는 옵티미스틱을 사용하시면 안됩니다 ‼️
예를들면, 결제후 잔여 금액같은 경우엔 옵티미스틱 UI를 사용하시면 안됩니다.


Optimistic-UI 사용해보기

좋아요를 올리는 api는 likeBoard 입니다.

import { useMutation, gql, useQuery } from "@apollo/client"

//좋아요 갯수 가지고 오는 api _ 게시글 조회 api에서 좋아요 갯수만 뽑아 옵니다. 
const FETCH_BOARD = gql`
	query fetchBoard($boardId: ID!) {
		fetchBoard(boardId: $boardId) {
			_id
			likeCount
		}
	}
`

//좋아요 카운트 올리는 api
const LIKE_BOARD = gql`
	mutation likeBoard($boardId:ID!) {
		likeBoard(boardId:$boardId)
	}
`

export default function() {
	const [likeBoard] = useMutation(LIKE_BOARD)
	const { data } = useQuery(FETCH_BOARD, { variables : { boardId : "게시글 아이디 넣어주세요!"}})
	
	const onClickOptimisticUI = () => {
		//likeBoard 뮤테이션 함수를 실행하겠습니다.
		likeBoard({
			variables: {
				boardId : "게시글 아이디 넣어주세요!"
			},

          // 응답을 받고난 후 받아온 응답을 다시 fetch 해줍니다. -> 느리고 효율적이지 못합니다.(백엔드에 요청을 한번더 해야하고 받아올때 까지 기다려야 합니다.)
          // refetchQueries: [
          //		{
          //			query: FETCH_BOARD,
          //			variables: { boardId : "//게시글 아이디 넣어주세요!" }
          // 		}
          // ]

          //옵티미스틱 UI -> 캐시를 바꾸고 캐시값을 받아오는걸 기다리지 않고 바로 바꿔줍니다.
			optimisticResponse: {
				likeBoard: (data?.fetchBoard.likeCount || 0)+1
			},
			// apollo 캐시를 직접 수정을 할 수 있었습니다.(백엔드 캐시가 아닙니다.) -> 느리지만 효율적입니다. (백엔드에 요청은 안하지만, 받아올때까지 기다려줘야 합니다.)
			update(cache,{data}) {
				// 이전 시간에는 modify를 사용했지만, 오늘은 writeQuery를 사용해보겠습니다.
				cache.writeQuery({
					query: FETCH_BOARD,
					variables: {boardId:'게시글 아이디 넣어주세요!'}
					//어떻게 수정할 것인지는 아래에 적어줍니다.
					data: {
						fetchBoard: {
							_id: '게시글 아이디 넣어주세요!',
							__typename: "Board"
							likeCount: data?.likeBoard
						}
					}
				})
			}
		})
	}

	return(
		<div>
            <h1>옵티미스틱 UI</h1>
            <div>현재카운트(좋아요):{data.fetchBoard.likeCount}</div>
            <button onClick={onClickOptimisticUI}>좋아요 올리기!!</button>
		</div>
	)
} 

이렇게 옵티미스틱 UI를 적용하면 컴퓨터 환경에 상관없이 유저 모두가 빠른 서비스를 이용하는 것 같이 속일 수 있습니다.

따라서 만족도는 상당히 많이 올라갈 수 있겠죠.

따라서 데이터가 중요하지 않고 실패할 확률이 없다면 옵티미스틱 UI를 사용해주시는게 좋습니다.

메타 태그와 Open Graph

어떤 링크를 공유했을 때 아래 사진과 같이 미리보기가 뜨는걸 보신 적 있으신가요?

이는 해당 링크에 대한 정보를 meta태그에 담아 미리볼 수 있도록 해준 것 입니다.

그럼 실제로 우리가 만들어볼까요?

//open graph head 파일 - 우리 페이지
import Head from 'next/head'

const OpengraphHeadPage = () => {
	return (
		<div>
			<Head>
				<meta property="og:title" content="내사이트"/>
				<meta property="og:description" content="환영합니다."/>
			</Head>
				<h1> 오픈 그래프 연습입니다~ </h1>
		</div>
	)
}

export default OpengraphHeadPage

이렇게 사이트를 만들어 사이트 주소를 카카오, 슬랙, 디스코드 같은 곳에 보내게 되면 우리가 위에서 만들어 뒀던 OG태그가 보이는 것 입니다.

우리가 구현해둔 OG태그가 보이는 이유는 카카오,슬랙,디스코드 개발자분들이 미리보기 기능을 모두 구현을 해두셨기 때문에 보이는 것 입니다.

우리가 구현한 OG태그를 미리보기로 구현해주는 과정을 아래에서 useEffect를 사용해 구현해 볼 것 인데 실제로는 백엔드에서 많이 이루어 집니다.

//open graph preview 파일 - 카카오,디스코드 개발자(우리페이지를 OG태그로 보여줄)
const OpengraphPreviewPage = () => {
	
	useEffect(()=>{
		const getOpenGraph = () => {
			const result = axios.get("어떤 사이트 주소")
			console.log(result.data)
		}
		getOpenGraph()
	},[])	

	return (
		<div>
		</div>
	)
}

export default OpengraphPreviewPage

이렇게 해주시면 우리가 원하는 html코드를 받아오는 것을 볼 수 있습니다.(axios의 결과물로 JSON이 아닌 html 소스코드를 받아오는 것 입니다.)

html코드를 모두 받아 왔으면 여기서 우리가 og관련된 내용을 뽑아와야 합니다 .

사실 이를 쉽게 뽑아올 수 있도록 도와주는 도구가 당연히 있습니다.

npm에 들어가 cheerio 또는 puppeteer 라고 검색해보시면 사용방법과 함께 자세히 나와있습니다. 사용해보실 분들은 Docs를 읽고 사용해보시는걸 권장합니다.
cheerio는 scraping 도구이며 puppeteer는 크롤링 도구 입니다.

보통 실무에서는 위와같은 라이브러리를 사용하지만 우리는 간단히 사용만해볼 것 이므로 직접 긁어와 보도록 하겠습니다.

//open graph preview 파일 _ 카카오,디스코드 개발자(우리페이지를 OG태그로 보여줄)
const OpengraphPreviewPage = () => {
	
	useEffect(() => {
		const getOpenGraph = () => {
			const result = axios.get("어떤 사이트 주소")
			//meta 태그 기준으로 자르기
			//console.log(result.data.split("<meta"))

			//og:으로 시작하는 애들만 filter하기
			//console.log(result.data.filter((el)=>el.includes("og:")))

			//og:으로 시작하는 애들만 filter하기
			const openGraph = result.data
					.split("<meta")
					.filter((el)=>el.includes("og:url"))[0]
					.split(" ")
			console.log(openGraph[1].replaceAll('content='',"").replaceAll('"/>',""))

		}
		getOpenGraph()
	},[])	

	return (
		<div>
		</div>
	)
}
              
export default OpengraphPreviewPage

이렇게 filter하고 자르며 열심히 긁어온 결과 콘솔에 링크가 예쁘게 뜨고 있습니다.

이제 OG태그에 긁어온 주소값을 넣어주시면 됩니다.

💡 백엔드의 응답결과
우리는 지금까지 백엔드의 결과물로 JSON이 날아온다고 알고있었습니다.
하지만 백엔드의 응답 결과물로 무조건 JSON이 날아오는 것은 아닙니다. JSON 이외에도 소스코드가 날아올 수 있습니다.


서버사이드 렌더링이 필요한 이유

OG태그에서의 서버사이드 렌더링

우리가 head태그를 하드코딩으로 만들어주면 어디서든 OG태그를 볼 수 있습니다.

하지만 OG태그를 언제나 하드코딩을 할 수 있는것은 아닙니다.

예를들어 상품 상세 페이지같은 경우에는 여러 페이지를 만드는것이 아닌 다이나믹 라우팅 처리를 하는 것 입니다.

따라서 각 상품에 대해 head태그를 삽입할 수 없는 상황인 것 입니다.

그럼, useQuery를 이용해 data를 불러오고 그 data를 meta태그에 넣어주는 방식으로는 안될까요?

import Head from 'next/head'

const FETCH_DATA = gql`
	//상품 상세를 가지고 오는 쿼리문
`

const OpengraphHeadPage = () => {
	const { data } = useQuery(FETCH_DATA)
	
	return(
		<div>
			<Head>
				<meta property="og:title" content=`${data.fetchBoard.contents}`/>
				<meta property="og:description" content="환영합니다."/>
			</Head>
        	<h1> 오픈 그래프 연습입니다~ </h1>
		</div>
	)
}

export default OpengraphHeadPage

이런식으로 data를 가지고 오고 가지고 온 data를 og 태그에 넣어 주었다고 가정해보겠습니다.

이렇게 되면 초기 렌더링을 했을때는 백엔드 요청을 하지 않기 때문에 메타 태그가 비어있습니다.

이후에 useQuery가 실행되고난 이후에야 meta 태그에 들어간 data.fetchBoard~ 이부분이 채워지게 되며 그제서야 데이터가 들어오게 됩니다.

그럼 아래 예제상황을 통해 조금 더 자세히 알아 보도록 하겠습니다.

위의 그림과 같이 카카오톡 유저가 있다고 가정해보겠습니다.

그리고 카카오톡에서 내 사이트의 링크를 공유했다고 가정해보겠습니다.

그럼 프론트 서버로부터 백엔드에서 axios 응답결과로 준 html를 받아올 것 입니다. 그리고 해당 소스에서 meta 태그를 찾는 것 입니다.

하지만 초기 렌더링에서는 useQuery까지 하지 않기때문에 meta 태그에 넣어두었던 data는 비어있는 상태가 되어있는 것 입니다.

이런 경우를 대비해서 서버사이드 렌더링이 필요한 것 입니다.

이런 경우 특정 주소에 서버사이드 렌더링 주소로 설정할 수 있습니다.

따라서 만일 서버사이드 렌더링 주소일 경우 프론트 서버에 요청을 하면 바로 응답을 보내는 것이 아닌 백엔드로 요청을 보내 데이터를 모두 꺼내와 합친 후 최종결과를 응답으로 전달해줍니다.

따라서 상품상세페이지같은 동적 페이지에 OG를 적용해주시고 싶으시다면 서버사이드 렌더링을 적용해주셔야 합니다.

SEO(Search Engin Optimization) - 검색 엔진 최적화

사실 서버사이드 렌더링이 필요한 이유를 검색하시면 검색 엔진 최적화(SEO)가 가장 많이 언급 됩니다.

검색엔진 이라고 함은 네이버, 구글, 다음등을 말하며 해당 사이트들은 자체적인 검색봇이 있습니다.

각각의 검색봇은 24간동안 여러 사이트를 돌아다니며 해당 사이트가 어떤 사이트인지 파악하는데, 서버사이드 렌더링이 아니면 초기렌더링을 해왔을 경우 useQuery가 안되어 있기 때문에 안에 데이터가 모두 텅텅 비어 있습니다.

이렇게 되면 검색봇은 페이지가 무슨페이지인지 모르겠죠.

하지만 서버사이드 렌더링을 하게되면 html,css,js 받아올 때 이미 데이터를 모두 완벽히 받아와 보여주기 때문에 검색봇이 해당 사이트가 무슨 사이트인지 알 수 있습니다.

💡 tip! 검색이 더 잘되게 하는 방법
시멘틱 태그
h1 과 같은 의미가 있는 태그를 사용하는 것이 중요합니다.
즉 웹접근성, 웹 표준을 잘 지켜서 코드를 작성하시는 것이 좋습니다.
또한 router는 검색엔진이 읽을 수 없지만, a 태그나 link 태그는 읽을 수 있기때문에 a 태그로 페이지 이동을 해준 부분은 페이지 간의 서로의 연관성을 읽을 수 있기때문에 검색에 유리합니다.

0개의 댓글