국비학원 36일차 : React, 클론코딩

Digeut·2023년 4월 14일
0

국비학원

목록 보기
30/44

CSS 추가

body {
    position: relative;
    background: #f5f6f7;
}

a {
    text-decoration: none;
    cursor: pointer;
}

h1 {
    display: block;
    font-size: 2em;
    margin-block-start: 0.67em;
    margin-block-end: 0.67em;
    margin-inline-start: 0px;
    margin-inline-end: 0px;
    font-weight: bold;

    margin: 0;
    padding: 0;
    -webkit-text-size-adjust: none;
}

h3 {
    display: block;
    font-size: 1.17em;
    margin-block-start: 1em;
    margin-block-end: 1em;
    margin-inline-start: 0px;
    margin-inline-end: 0px;
    font-weight: bold;
}

form{
    display: block;
    margin: 0;
    padding: 0;
    -webkit-text-size-adjust: none;
}

div{
    display: block;
}

label {
    cursor: pointer;
}

input{
    border-radius: 0;
    appearance: none;
    outline: 0;
    text-decoration: none;
    cursor: pointer;
    -webkit-text-size-adjust: none;
}

option {
    font-weight: normal;
    display: block;
    white-space: nowrap;
    min-height: 1.2em;
    padding: 0px 2px 1px;
}

select{
    border-radius: 0;
    border: none;
    background: 0 0;

    appearance: none;
    outline: 0;
    text-decoration: none;
    cursor: pointer;
    -webkit-text-size-adjust: none;
}

ul {
    margin: 0 0 9px 0;
    -webkit-text-size-adjust: none;
}

li {
    text-align: -webkit-match-parent;
}

#container, #footer, #header {
    margin: 0 auto;
    max-width: 768px;
    min-width: 460px;
}

#header {
    position: relative;
    overflow: hidden;
    padding: 60px 0 54px;
    box-sizing: border-box;
}

#container {
    width: auto;
    max-width: 768px;
    margin: 0 auto;
    /* max-width: 768px; */
    min-width: 460px;
}

#footer {
    clear: both;
    margin: 0 auto;
    padding: 30px 0 15px 0;
    text-align: center;
}

#footer * {
    font-size: 12px;
    font-style: normal;
    line-height: normal;
    margin: 0;
    padding: 0;
    list-style: none;
    color: #333;
}

#footer ul li:first-child {
    background: 0 0;
}

#footer ul li {
    font-size: 12px;
    position: relative;
    display: inline;
    padding: 0 3px 0 7px;
    white-space: nowrap;
    background: url(https://static.nid.naver.com/images/join/pc/bu_bar_2x.gif) left 50% no-repeat;
    background-size: 1px 10px;
}

/* @media (max-width: 1024px) */
#footer a, #footer a:visited {
    color: #7e7e7e;
}

#footer address {
    font: 9px/14px Verdana;
}

:root #footer address .logo {
    top: 2px;
    margin-left: 1px;
}

#footer address .logo {
    position: relative;
    display: inline-block;
    width: 63px;
    height: 12px;
    background: url(https://static.nid.naver.com/images/ui/login/pc_sp_login_190522.png) no-repeat;
    background-position: -242px 0;
    background-size: 460px auto;
}

#footer address span {
    font: 9px/14px Verdana;
    padding-left: 2px;
}

#footer address em {
    font: 9px verdana;
    padding-left: 4px;
}

#footer address a {
    font: bold 9px Verdana;
    color: #333;
}

#footer a {
    text-decoration: none;

}

.join_membership {
    padding-bottom: 20px;
}


.h_logo {
    display: block;
    margin: 0 auto;
    width: 240px;
    height: 44px;
    background-image: url(https://static.nid.naver.com/images/ui/join/m_naver_logo_20191126.png);
    background-repeat: no-repeat;
    background-position: 0 0;
    background-size: 240px auto;
    color: transparent;
    font-size: 0;
}

.blind {
    position: absolute;
    overflow: hidden;
    clip: rect(0 0 0 0);
    margin: -1px;
    width: 1px;
    height: 1px;
}

.row_group {
    overflow: hidden;
    width: 100%
}

.row_group+.row_group {
    margin-top: 20px;
}

.join_title {
    margin: 19px 0 8px;
    font-size: 14px;
    font-weight: 700;
}

.join_content{
    width: 460px;
    height: 995.60px;
}

.step_url {
    position: absolute;
    top: 16px;
    right: 13px;
    font-size: 15px;
    line-height: 18px;
    color: #8e8e8e;
}

.ps_box, .ps_box_disable {
    display: block;
    position: relative;
    width: 100%;
    height: 51px;
    border: solid 1px #dadada;
    padding: 10px 110px 10px 14px;
    background: #fff;
    box-sizing: border-box;
    /* vertical-align: top; */
}

.ps_box.int_id {
    background: #fff;
    outline: 0;
}

.ps_box.int_pass{
    padding-right: 40px;
}

.ps_box.int_pass_check {
    padding-right: 40px;
}



.int {
    display: block;
    position: relative;
    width: 100%;
    height: 29px;
    padding-right: 25px;
    line-height: 29px;
    border: none;
    background: #fff;
    font-size: 15px;
    box-sizing: border-box;
    z-index: 10;
}

.ps_box.int_pass:after{
    content: '';
    display: inline-block;
    position: absolute;
    top: 50%;
    right: 13px;
    width: 24px;
    height: 24px;
    margin-top: -12px;
    background-image: url(https://static.nid.naver.com/images/ui/join/m_icon_pw_step.png);
    background-repeat: no-repeat;
    background-position: 0 0;
    background-size: 125px 75px;
    cursor: pointer;
}

.ps_box.int_pass_check:after{
    content: '';
    display: inline-block;
    position: absolute;
    top: 50%;
    right: 13px;
    width: 24px;
    height: 24px;
    margin-top: -12px;
    background-image: url(https://static.nid.naver.com/images/ui/join/m_icon_pw_step.png);
    background-repeat: no-repeat;
    background-size: 125px 75px;
    background-position: -27px 0;
    cursor: pointer;
}

.box_right_space {
    padding-right: 14px;
    box-sizing: border-box;
}

.join_row.join_birthday{
    padding: 0;
}

.bir_wrap{
    display: table;
    width: 100%;
}

.bir_dd, .bir_mm, .bir_yy {
    display: table-cell;
    table-layout: fixed;
    width: 147px;
    vertical-align: middle;
}

.join_birthday .ps_box {
    padding: 11px 14px;
}

.bir_mm+.bir_dd, .bir_yy+.bir_mm {
    padding-left: 10px;
}

:root .sel {
    background: #fff url(https://static.nid.naver.com/images/join/pc/sel_arr_2x.gif) 100% 50% no-repeat;
    background-size: 20px 8px;
}

.sel {
    width: 100%;
    height: 29px;
    padding: 7px 8px 6px 7px\9;
    font-size: 15px;
    line-height: 18px;
    color: #000;
    border: none;
    border-radius: 0;
}

.join_sex {
    overflow: hidden;
}

.join_sex .gender_code {
    display: block;
    width: 100%;
    padding-right: 7px;
    box-sizing: border-box;
}

.terms_choice {
    color: #8e8e8e;
}

.join_email .terms_choice {
    font-size: 12px;
    font-weight: 400;
}

.row_group+.join_mobile {
    margin-top: 20px;
}

.join_mobile {
    position: relative;
    overflow: hidden;
}

.join_mobile .int_mobile_area {
    position: relative;
    margin-top: 10px;
    padding: 0 125px 0 0;
}

.gender_nationality .gender_code, .join_mobile .country_code, .join_sex .gender_code {
    display: block;
    width: 100%;
    padding-right: 7px;
    box-sizing: border-box;
}

.country_code {
    position: relative;
    overflow: hidden;
}

.join_mobile .int_mobile {
    display: inline-block;
    width: 100%;
    padding: 10px 15px 10px 14px;
    vertical-align: top;
}

.ps_box .lbl {
    left: 0;
    padding: 0 14px;
    width: 100%;
    box-sizing: border-box;
}

.lbl {
    display: block;
    position: absolute;
    top: 50%;
    margin-top: -9px;
    font-size: 15px;
    line-height: 18px;
    color: #8e8e8e;
    z-index: 9;
}

.join_mobile .int_mobile_area .btn_verify {
    position: absolute;
    top: 0;
    right: 0;
    width: 115px;
    height: 51px;
    padding: 18px 0 16px;
    font-weight: 700;
    text-align: center;
    box-sizing: border-box;
    text-decoration: none;
}

.btn_verify {
    display: block;
    font-size: 15px;
    cursor: pointer;
}

.btn_primary {
    color: #fff;
    border: solid 1px rgba(0,0,0,.08);
    background-color: #03c75a;
}

.join_mobile .ps_box_disable, .join_mobile .ps_box_disable input {
    background: #f7f7f7;
    outline: 0;
}

.int_mobile_area+.ps_box, .int_mobile_area+.ps_box_disable, .ps_box_disable+.ps_box {
    margin-top: 10px;
}

.wa_blind {
    position: absolute;
    overflow: hidden;
    clip: rect(0 0 0 0);
    margin: -1px;
    width: 1px;
    height: 1px;
    font-size: 0;
}

.btn_area {
    margin: 30px 0 9px;
}

.btn_type {
    display: block;
    width: 100%;
    padding: 15px 0 15px;
    font-size: 18px;
    font-weight: 700;
    text-align: center;
    cursor: pointer;
    box-sizing: border-box;
}

.copy{
    display: none;
}

address .u_cri {
    display: inline-block;
    font-size: 12px;
    font-family: Arial,Helvetica,sans-serif;
    font-weight: 400;
    color: #7e7e7e;
    margin: 0;
    padding-left: 1px;
}

#footer address .u_cra{
    display: inline-block;
    font-size: 12px;
    font-family: Arial,Helvetica,sans-serif;
    font-weight: 400;
    color: #7e7e7e;
}

/* #footer .u_cr, #footer address .all_r, #footer address .copy, #footer address .logo {
    display: none;
} */


정렬이랑 폰트가 조금 다른것 빼고는 다 구색은 맞췄다...
정렬 왜 안될까 이것도 class이름이 좀 다르게 설정되어있는지 확인해봐야함...

React

Component, View 폴더 생성 이유

App.tsx의 코드들을 각각 Component와 View폴더를 만들어서 분리를 해주 었는데 그 이유를 Chat-GPT에서 찾아봤다

React에서 컴포넌트는 UI를 만드는 데 사용됩니다. 컴포넌트는 일반적으로 작고 독립적이며, 여러 번 재사용될 수 있습니다. 대규모 React 애플리케이션에서는 수백 개 이상의 컴포넌트를 가질 수 있으며, 이러한 컴포넌트들을 유지 보수하고 관리하기 위해 구조화된 디렉토리 구조가 필요합니다.

이러한 이유로 React 개발자들은 컴포넌트와 관련된 파일을 별도의 폴더로 구성하곤 합니다. 이 구조는 프로젝트의 규모가 커질수록 더 유용하며, 컴포넌트 파일의 위치를 파악하기 쉽게 만들어 줍니다.

View 폴더는 일반적으로 애플리케이션의 레이아웃과 스타일링을 관리하는 데 사용됩니다. 이 폴더 안에는 컴포넌트의 스타일링 파일, 이미지 파일 및 기타 관련 파일을 포함할 수 있습니다. 이는 컴포넌트와 뷰가 서로 분리되어 있음을 의미합니다. 따라서 애플리케이션의 레이아웃을 변경하려면 뷰 폴더의 파일을 수정하면 됩니다. 이는 유지 보수와 개발 과정을 간단하게 만들어 줍니다.

결론적으로, React에서 컴포넌트와 뷰 폴더를 사용하여 프로젝트를 구성하는 것은 애플리케이션의 유지 보수성과 개발자의 생산성을 향상시키는 방법 중 하나입니다.

React 프로젝트 구조(TypeScript 사용)

example-react/
  node_modules/
  public/
    index.html
    favicon.ico
  src/
    components/
      Component1.tsx
      Component2.tsx
      ...
    views/
      Home.tsx
      About.tsx
      ...
    App.tsx
    index.tsx
    types/
      types.d.ts
  tsconfig.json
  package.json
  README.md
  1. node_modules: 프로젝트에서 사용하는 모든 종속성이 설치되는 디렉토리입니다. 보통 개발자가 직접 수정하지 않습니다.
  2. public: 정적 파일(HTML, 이미지 등)이 위치하는 디렉토리입니다.
    index.html: React 애플리케이션의 진입점입니다. 이 파일은 애플리케이션의 시작점으로 사용됩니다.
    favicon.ico: 웹 페이지의 아이콘을 지정하는 이미지 파일입니다.
  3. src: 실제 소스 코드가 위치하는 디렉토리입니다.
  • components: React 컴포넌트가 위치하는 디렉토리입니다. 보통 각 컴포넌트는 자체 파일을 갖습니다.
  • views: React 컴포넌트를 조합하여 페이지를 만드는 데 사용되는 디렉토리입니다. 보통 각 페이지는 자체 파일을 갖습니다.
  • App.js: 애플리케이션의 루트 컴포넌트입니다. 이 파일에서 라우팅 및 전역 상태 관리 등을 설정합니다.
  • index.js: 애플리케이션을 렌더링하는 진입점입니다. 이 파일에서는 ReactDOM.render() 메서드를 사용하여 애플리케이션을 실제 DOM에 렌더링합니다.
  • types 디렉토리: TypeScript에서 자주 사용되는 인터페이스, 타입 등을 모아놓은 파일이 위치하는 디렉토리입니다. 이 디렉토리는 일반적으로 types 또는 typings라는 이름으로 사용됩니다.
  1. tsconfig.json: TypeScript 컴파일러 설정 파일입니다. 이 파일을 통해 TypeScript 컴파일러의 동작을 제어할 수 있습니다. 예를 들어, ECMAScript 버전, 모듈 시스템, 소스 맵 등을 설정할 수 있습니다.
  2. package.json: 프로젝트 정보, 종속성 및 스크립트 등이 포함된 파일입니다.
  3. README.md: 프로젝트에 대한 설명이 포함된 파일입니다.

Snippets

스니펫(Snippet)은 코드 조각이나 템플릿 등을 미리 작성해두고, 필요할 때 불러와서 사용하는 기능을 말합니다. 주로 코드 에디터나 IDE에서 지원하며, 자주 사용되는 코드나 반복적으로 작성해야 하는 코드를 미리 등록해두고, 해당 코드를 자동으로 완성하거나 불러와서 사용할 수 있습니다. 스니펫은 코드 작성 시간을 절약하고, 오타나 문법 오류를 줄여줘서 개발 생산성을 높일 수 있습니다. 대부분의 코드 에디터와 IDE에서 스니펫 기능을 제공하며, 사용자가 직접 스니펫을 추가하거나 수정할 수도 있습니다.

스니펫은 주로 일정한 형식을 갖는 코드 블록을 자주 사용해야 할 때 유용합니다. 예를 들어, 함수 선언문이나 반복문 등의 코드 블록을 자주 사용해야 할 경우, 스니펫을 이용하여 미리 작성해두면 해당 코드 블록을 반복해서 작성할 필요 없이, 스니펫을 호출하여 쉽게 사용할 수 있습니다. 스니펫은 코드 블록뿐만 아니라, 템플릿이나 라이브러리 등을 포함한 여러 가지 형태로 등록할 수 있습니다.

스니펫은 대개 키워드나 축약어 등의 단축키를 이용하여 호출합니다. 예를 들어, for라는 단축어를 입력하면 for문의 스니펫이 자동완성되는 등의 기능이 제공됩니다. 각 에디터나 IDE마다 지원하는 스니펫의 종류와 사용 방법은 다를 수 있으며, 각각의 사용자 매뉴얼을 참고하여 사용할 수 있습니다.

VS Code에서 스니펫 자동완성 명령어 = rfc

export default와 export의 차이점

export와 export default는 모듈 시스템에서 사용되는 키워드로, 모듈 내부의 함수, 클래스, 객체 등을 다른 모듈에서 사용할 수 있도록 내보내는 역할을 합니다.

export는 모듈에서 여러 개의 변수, 함수, 클래스 등을 내보낼 때 사용합니다. 내보내려는 변수, 함수, 클래스 등 앞에 export 키워드를 붙이면 다른 모듈에서 해당 변수, 함수, 클래스 등을 불러와 사용할 수 있습니다. export로 내보낸 항목을 불러올 때는 중괄호({})를 사용하여 해당 항목의 이름을 명시해야 합니다.

// 모듈에서 변수, 함수, 클래스 등을 내보내는 예시
export const name = "Alice";
export function greet() {
  console.log(`Hello, ${name}!`);
}
export class Person {
  constructor(name) {
    this.name = name;
  }
}

export default는 모듈에서 기본으로 내보내는 항목을 지정할 때 사용합니다. 내보내려는 항목 앞에 export default를 붙이면 다른 모듈에서 해당 항목을 불러올 때 이름을 지정하지 않아도 됩니다. 즉, 기본으로 내보내는 항목은 이름이 없고, 내보내려는 항목을 불러올 때는 원하는 이름을 지정하여 사용할 수 있습니다. export default는 하나의 모듈에서 한 번만 사용할 수 있습니다.

// 모듈에서 기본으로 내보내는 항목을 지정하는 예시
export default function add(a, b) {
  return a + b;
}

다른 모듈에서 export로 내보낸 항목을 불러올 때는 중괄호({})를 사용하여 이름을 지정해야 하지만, export default로 내보낸 항목은 이름을 지정하지 않아도 됩니다. 다음은 export와 export default로 내보낸 항목을 불러올 때의 차이점을 보여줍니다.

// 다른 모듈에서 export로 내보낸 항목을 불러올 때
import { name, greet, Person } from "./module.js";

// 다른 모듈에서 export default로 내보낸 항목을 불러올 때
import add from "./module.js";
import Header from './view/HeaderView'; 
import Footer from './view/FooterView';
import Main from './view/MainView';

Component

react를 통해서 기존의 html의 반복되는 코드들을 component로
나눠서 (java에서 for반복문처럼)간단하게 표현할수 있다.

<div class="id-password-wrapper">
	<div class="input-row">
		<div class="icon-shell">
			<span class="icon-id">
				<span class="blind">아이디</span>
			</span>
		</div><input type="text" class="input-text" maxlength="41" placeholder="아이디" name="id" id="id" />
	</div>
	<div class="input-row">
		<div class="icon-shell">
			<span class="icon-pw">
				<span class="blind">비밀번호</span>
			</span>
		</div><input type="password" class="input-text" maxlength="16" placeholder="비밀번호" name="pw" id="pw" />
	</div>
</div>
<ul class="find-wrapper">
	<li>
		🌟<a class="find-text" href="https://nid.naver.com/user2/api/route?m=routePwInquiry&lang=ko_KR">비밀번호 찾기</a>
	</li>
	<li>
		🌟<a class="find-text" href="https://nid.naver.com/user2/api/route?m=routeIdInquiry&lang=ko_KR">아이디 찾기</a>
	</li>
	<li>
		🌟<a class="find-text" href="https://nid.naver.com/user2/V2Join?m=agree&lang=ko_KR">회원가입</a>
	</li>
</ul>

⭐,🌟의 부분은 반복이 되게 된다. 이런 반복되는 코드를 component폴더를 만들어서 따로 분리를 진행했다.

InputComponent

import { Dispatch, SetStateAction } from 'react'

interface Props{
    label: string;
    type: string;
    name: string;
    setter: Dispatch<SetStateAction<string>>;
    maxLength: number;
    iconClass: string;
}

export default function 💡NaverInput(props: Props) {

const {label, type, name, maxLength, iconClass, setter} = props;
  return ( //반복되는 값 빼주고, 어떤 변수 써야할지 정한다 
  			//: 아이디, type ... 6개의 변수 잡기
    <div className="input-row">
		<div className="icon-shell">
			<span className={iconClass}>
				<span className="blind">{label}</span>
			</span>
		</div>
		<input type={type}
		className="input-text" 
        maxLength={maxLength} 
        placeholder={label}
        name= {name}
		onChange={(event) => setter(event.target.value)}/>
	</div>
  )
}

MainView

<div className="id-password-wrapper">

	<NaverInput  label = "아이디" type="text" name = "id" maxLength={41} iconClass="icon-id" setter={setId}/>
	<NaverInput label = "비밀번호" type="password" name = "pw" maxLength={16} iconClass="icon-pw" setter={setPassword}/>

                  
</div>

FindComponent

import React from 'react'

interface Props{
    title: string;
    link: string;
}

export default function NaverFind({title, link}: Props) {
  return (
    <li>
          <a className={"find-text" }
          href={link}>
            {title}</a>
    </li>
       
  )
}

MainView
FIND_LIST 생성

const FIND_LIST = [
    {
        title: '비밀번호 찾기',
        link: 'https://nid.naver.com/user2/api/route?m=routePwInquiry&lang=ko_KR',

    },
    {
        title: '아이디 찾기',
        link: 'https://nid.naver.com/user2/api/route?m=routeIdInquiry&lang=ko_KR',

    },
    {
        title: '회원가입',
        link: 'https://nid.naver.com/user2/V2Join?m=agree&lang=ko_KR',

    },

];

만들어진 리스트를 함수를 통해 찾아서 가져와 반환하는 코드를 작성

<ul className="find-wrapper">
	{
		FIND_LIST.map((findItem) => (<💡NaverFind title = {findItem.title} link={findItem.link}/>))
       //요소를 하나씩 꺼내온걸 함수로 처리한 다음 반복되는 값을 뿌려주게 된다.
	}
</ul>

View

Header, Main, Footer을 각각 View폴더를 만들어 나눠서 분리했다.

HeaderView


export default function Header() {
    return (
        <div className="header">
            
            <<div className="header-inner">
                <a href="https://naver.com" className="logo">
                    <h1 className="blind">NAVER</h1>
                </a>
                <div className="lang">
                    <select className="select">
                        <option>한국어</option>
                        <option>English</option>
                    </select>
                </div>
            </div>
        </div>
      );
}

MainView

import NaverFind from "../../component/FindComponent";
import NaverInput from "../../component/InputComponent";
//export default한 항목을 불러와서 {}각각 요소 적지않음
import {useRef, useState} from 'react'

const FIND_LIST = [
    {
        title: '비밀번호 찾기',
        link: 'https://nid.naver.com/user2/api/route?m=routePwInquiry&lang=ko_KR',

    },
    {
        title: '아이디 찾기',
        link: 'https://nid.naver.com/user2/api/route?m=routeIdInquiry&lang=ko_KR',

    },
    {
        title: '회원가입',
        link: 'https://nid.naver.com/user2/V2Join?m=agree&lang=ko_KR',

    },

];

export default function Main() {
    const formRef = useRef<HTMLFormElement | null>(null);

    const[id,setId]=useState<string>(''); //이거 위치 여기 맞니
    const [password, setPassword] = useState<string>(''); //이거 언제 추가했대
    const [isIdCheck, setIdCheck] = useState<boolean>(false);
  
    const onSubmitHandler = () => {
      if(id.trim()){
        setIdCheck(true);
        return;
      }
      setIdCheck(false);
      
      if(formRef.current) formRef.current.submit();
    }
   
    return (
    <div className="main">
      <div className="content">
        <div className="sign-in-wrapper">
        <form ref={formRef} id="form" action="https://nid.naver.com/nidlogin.login" method="POST">
          <ul className="panel-wrapper">
            <li className="panel-item">
              <div className="panel-inner">
  
                <div className="id-password-wrapper">

                    <NaverInput  label = "아이디" type="text" name = "id" maxLength={41} iconClass="icon-id" setter={setId}/>
                    <NaverInput label = "비밀번호" type="password" name = "pw" maxLength={16} iconClass="icon-pw" setter={setPassword}/>

                  
                </div>
  
                <div className="sign-in-keep-wrapper">
                  <div className="keep-check">
                    <input type="checkbox" className="input-keep" value="off" />
                    <label className="keep-text">로그인 상태 유지</label>
                  </div>
                  <div className="ip-check"></div>
                </div>
  
                {isIdCheck && (<div id="sign-in-error" className="sign-in-error">
                  <div className="error-message">
                    <strong>아이디</strong>를 입력하세요
                  </div>
                </div>)}
                
                <div className="sign-in-button-wrapper">
                  <button type="button" className="sign-in-button" onClick = {() => onSubmitHandler()}>
                    <span className="button-text">로그인</span>
                  </button>
                </div>
              </div>
            </li>
          </ul>
        </form>
      </div>
  
      <ul className="find-wrapper">
        {
            FIND_LIST.map((findItem) => (<NaverFind title = {findItem.title} link={findItem.link}/>))
            //요소를 하나씩 꺼내온걸 함수로 처리한 다음 반복되는 값을 뿌려주게 된다.
        }
      </ul>
  
      <div className="banner-wrapper">
        <div className="banner-content">
          <img className="banner-img" src="https://ssl.pstatic.net/melona/libs/1378/1378592/fe1b4bd9453e84b57ed7_20230407151920279.jpg" />
        </div>
      </div>
    </div>
    </div>
    );
}

FooterView

export default function Footer() {
    return(
        <div className="footer">
            <div className="footer-inner">
              <ul className="footer-link">
                <li><a className="footer-item"><span className="text">이용약관</span></a></li>
                <li><a className="footer-item"><span className="text"><strong>개인정보처리방침</strong></span></a></li>
              <li><a className="footer-item"><span className="text">책임의 한계와 법적고지</span></a></li>
              <li><a className="footer-item"><span className="text">회원정보 고객센터</span></a></li>
            </ul>
            <div className="footer-copy">
              <a href="https://naver.com">
                <span className="footer-logo">
                  <span className="blind">네이버</span>
                </span>
              </a>
              <span className="text">Copylight</span>
              <span className="copy">© NAVER Corp.</span>
              <span className="text">All Rights Reserved.</span>
            </div>
          </div>
        </div >
      );
}

모르는 함수 다시 분석해보기
map관련

profile
개발자가 될 거야!

0개의 댓글