관리 메뉴

CASSIE'S BLOG

[비공개] 원본... 본문

PROGRAMMING/React

[비공개] 원본...

ITSCASSIE1107 2023. 10. 9. 17:27

 

 

 

 

const skillData = {
    skillList: [
        {
            label: "HTML/CSS",
            detail: ["원하는 UI를 실용적이고, 익숙하게 만들어 낼 수 있습니다.", "빌드시스템(Gulp)과 템플릿 엔진(ejs, jade)의 사용이 가능합니다.", "Css 최신 문법이 숙지되어 있고 Css 프리프로세서 Sass를 사용할 수 있습니다."],
            icon: "fab fa-html5",
        },
        {
            label: "jQuery",
            detail: ["다양한 속성과 메소드를 능숙하게 사용이 가능합니다.", "Ajax을 활용하여 비동기 통신으로 데이터 요청을 할 수 있습니다.", "jQuery UI 라이브러리를 활용할 수 있습니다."],
            icon: "fab fa-node-js",
        },
        {
            label: "Javascript/Typescript",
            detail: ["ES6와 이후의 자바스크립트 문법을 사용할 수 있습니다.", "Vanilla JS의 웹 제작이 가능하고 필요 이유에 대해 충분히 이해하고 있습니다.", "타입에 대한 이해를 하고 있고, 인터페이스의 사용이 가능합니다."],
            icon: "fab fa-js-square",
        },
        {
            label: "React",
            detail: ["컴포넌트 생명주기와 속성을 사용할 수 있습니다.", "class, function 컴포넌트의 차이를 이해하고 있으며 구분하여 사용할 수 있습니다.", "리덕스를 사용하여 상태관리를 할 수 있습니다."],
            icon: "fab fa-react",
        },
        {
            label: "Node.js",
            detail: ["Node.js가 작동하는 법에 대해 이해하고 있습니다.", "express 프레임워크를 사용하여 Restful API 서버를 만들 수 있습니다.", "모델, 라우터, 컨트롤러, 서비스를 나누어 구조화된 서버를 만들 수 있습니다."],
            icon: "fab fa-node",
        },
        {
            label: "Git/Github",
            detail: ["git flow가 무엇인지 알고 있으며 왜 사용해야 하는지 이해하고 있습니다.", "깃과 깃헙을 사용하여 다른 개발자들과 협업을 할 수 있습니다.", "깃 리베이스를 할 수 있으며 스쿼시를 통한 커밋 관리 경험이 있습니다."],
            icon: "fab fa-git-square",
        },
    ],

    experienceList: [
        {
            period: "2018.11 - PRESENT",
            position: "WEB DEVELOPER",
            company: "CONER CREATIVE",
            explain: "에이전시 회사인 코너크리에이티브에서 사이트 제작과 유지보수 운영을 담당하였습니다. 삼성, 기아, 캐논 등 대기업 솔루션 경험을 익히며 폭넓은 인터렉션 제작 능력을 키웠습니다. ",
        },
        {
            period: "2018.09 - 2018.11",
            position: "PUBLISHER",
            company: "FIVESENSE SOFT",
            explain: "웹 에이전시 오감소프트에서 퍼블리싱 작업을 담당하였습니다. 그누보드를 기반으로 개발된 솔루션을 활용하여 웹 사이트를 제작 및 유지운영을 하였습니다. 도메인 기관으로 고도몰을 사용하였고, 기본적인 php 문법을 숙지하였습니다. ",
        },
        {
            period: "2017.09 - 2018.03",
            position: "E-BOOK DEVELOPER",
            company: "THE MOUM",
            explain: "플래시로 제작되었던 E-BOOK을 HTML을 기반의 웹 페이지 전자교과서 제작을 하였습니다. 지학사, 천재교육, 동아 출판사의 검정교과서 5권을 제작하였습니다.",
        },
    ],

    educationList: [
        {
            period: "2019.08 - 2019.09",
            position: "리액트(React.js)프로그래밍과정",
            company: "한국소프트웨어인재개발원",
            explain: "React.JS의 편리한 반응형 렌더링과 상태를 관리하는 캡슐화된 컴포넌트 개발 기술을 습득하였습니다. Vitual DOM을 활용한 랜더링의 비효율성을 최소화 하기 위하여 힘썼습니다.",
        },
        {
            period: "2019.07 - 2019.08",
            position: "노드JS(Node.js)프로그래밍 과정",
            company: "한국소프트웨어인재개발원",
            explain: "Node.js를 이용하여 비동기 통신 방식의 자바스크립트 프로그램 제작에 대해 익혔습니다. MongoDB등을 연결하여 데이터의 처리와 분석능력을 배웠고 동적 브라우저 뷰 구축 능력을 개발하였습니다.",
        },
        {
            period: "2016.11 - 2017.05",
            position: "(NCS)스마트웹&앱콘텐츠제작 양성과정",
            company: "더조은컴퓨터학원",
            explain: "GUI 디자인 가이드를 바탕으로 UI 구현 표준을 수립하고 UI를 제작하는 법을 학습하였습니다. 동시에 구현된 UI를 검증하기 위하여 사용성 테스트 계획, 수행, 분석, 결과 보고를 수행하는 역량을 길렀습니다.",
        },
    ],
};

export default skillData;

 

idx

 

import React from "react";
import data from "../../assets/data/skilldata";
import styled from "styled-components";
import Heading from "../atoms/Heading";

function SkillsWrap() {
    const skillsList = data.skillList.map((list, idx) => (
        <div className="skill-list" key={idx}>
            <Heading level="3" className="skill-label">
                <i className={list.icon}></i>: {list.label}
            </Heading>

            <ul className="skill-detail">
                {list.detail.map((detail, idx) => (
                    <li key={idx}>{detail}</li>
                ))}
            </ul>
        </div>
    ));
    return <StyledSkillsWrap>{skillsList}</StyledSkillsWrap>;
}

const StyledSkillsWrap = styled.div`
    display: flex;
    flex-wrap: wrap;
    .skill-list {
        width: calc(33% - 30px);
        margin: 0 15px 30px;
        padding: 20px 30px 20px 30px;
        border: 1px solid #252525;
        border-radius: 5px;
        cursor: default;
        .skill-label {
            margin-bottom: 10px;
            i {
                color: ${(props) => props.theme.mainColor};
                padding-right: 10px;
                font-size: 50px;
                vertical-align: text-top;
            }
        }
        .skill-detail {
            li {
                position: relative;
                padding: 0 0 10px 30px;
                font-size: 14px;
                line-height: 1.5;
                color: rgb(255 255 255 / 80%);
                &::before {
                    content: "";
                    position: absolute;
                    left: 0;
                    top: 13px;
                    width: 20px;
                    height: 1px;
                    background: ${(props) => props.theme.mainColor};
                }
            }
        }
    }
    @media ${(props) => props.theme.laptop} {
        width: 85%;
        margin: auto;
        .skill-list {
            width: calc(50% - 30px);
        }
    }
    @media ${(props) => props.theme.mobile} {
        width: 90%;
        .skill-list {
            width: calc(100% - 30px);
        }
    }
    .skill-list {
        position: relative;
        &::before,
        &::after {
            content: "";
            box-sizing: inherit;
            position: absolute;
            width: 100%;
            height: 100%;
            border: 1px solid transparent;
            width: 0;
            height: 0;
            border-radius: 5px;
        }
        &::before {
            top: 0;
            left: 0;
        }
        &::after {
            bottom: 0;
            right: 0;
        }
        &:hover {
            &::before,
            &::after {
                width: 100%;
                height: 100%;
            }
            &::before {
                border-top-color: ${(props) => props.theme.mainColor};
                border-right-color: ${(props) => props.theme.mainColor};
                transition: width 0.15s ease-out, height 0.15s ease-out 0.15s;
            }
            &::after {
                border-bottom-color: ${(props) => props.theme.mainColor};
                border-left-color: ${(props) => props.theme.mainColor};
                transition: border-color 0s ease-out 0.3s, width 0.15s ease-out 0.3s, height 0.15s ease-out 0.45s;
            }
        }
    }
`;

export default SkillsWrap;

 

idx는 .map() 함수에서 현재 반복 중인 요소의 인덱스를 나타내는 변수입니다. 여기서 idx는 각 스킬 항목을 고유하게 식별하기 위한 키(key)로 사용됩니다.

React에서 리스트(배열)의 각 항목에 고유한 key 속성을 제공하는 것은 중요합니다. key는 React가 요소를 효율적으로 관리하고 업데이트할 수 있도록 돕는 역할을 합니다. React는 key를 사용하여 각 항목이 변경되었는지 식별하고, 변경된 항목만 다시 렌더링합니다.

따라서 idx는 map() 함수에서 현재 스킬 항목을 나타내는 요소의 인덱스로 사용되고, 각 스킬 항목을 고유하게 식별하기 위한 목적으로 사용됩니다. React 컴포넌트의 key 속성은 일반적으로 요소의 고유성을 나타내는데 사용되며, 데이터가 변경될 때 React가 컴포넌트를 올바르게 업데이트할 수 있도록 도와줍니다.

 

 

 

import React from "react";
import data from "../../assets/data/skilldata";
import styled from "styled-components";
import Heading from "../atoms/Heading";

function SkillsWrap() {
    const skillsList = data.skillList.map((list, idx) => (
        <div className="skill-list" key={idx}>
            <Heading level="3" className="skill-label">
                <i className={list.icon}></i>: {list.label}
            </Heading>

            <ul className="skill-detail">
                {list.detail.map((detail, idx) => (
                    <li key={idx}>{detail}</li>
                ))}
            </ul>
        </div>
    ));
    return <StyledSkillsWrap>{skillsList}</StyledSkillsWrap>;
}

const StyledSkillsWrap = styled.div`
    display: flex;
    flex-wrap: wrap;
    .skill-list {
        width: calc(33% - 30px);
        margin: 0 15px 30px;
        padding: 20px 30px 20px 30px;
        border: 1px solid #252525;
        border-radius: 5px;
        cursor: default;
        .skill-label {
            margin-bottom: 10px;
            i {
                color: ${(props) => props.theme.mainColor};
                padding-right: 10px;
                font-size: 50px;
                vertical-align: text-top;
            }
        }
        .skill-detail {
            li {
                position: relative;
                padding: 0 0 10px 30px;
                font-size: 14px;
                line-height: 1.5;
                color: rgb(255 255 255 / 80%);
                &::before {
                    content: "";
                    position: absolute;
                    left: 0;
                    top: 13px;
                    width: 20px;
                    height: 1px;
                    background: ${(props) => props.theme.mainColor};
                }
            }
        }
    }
    @media ${(props) => props.theme.laptop} {
        width: 85%;
        margin: auto;
        .skill-list {
            width: calc(50% - 30px);
        }
    }
    @media ${(props) => props.theme.mobile} {
        width: 90%;
        .skill-list {
            width: calc(100% - 30px);
        }
    }
    .skill-list {
        position: relative;
        &::before,
        &::after {
            content: "";
            box-sizing: inherit;
            position: absolute;
            width: 100%;
            height: 100%;
            border: 1px solid transparent;
            width: 0;
            height: 0;
            border-radius: 5px;
        }
        &::before {
            top: 0;
            left: 0;
        }
        &::after {
            bottom: 0;
            right: 0;
        }
        &:hover {
            &::before,
            &::after {
                width: 100%;
                height: 100%;
            }
            &::before {
                border-top-color: ${(props) => props.theme.mainColor};
                border-right-color: ${(props) => props.theme.mainColor};
                transition: width 0.15s ease-out, height 0.15s ease-out 0.15s;
            }
            &::after {
                border-bottom-color: ${(props) => props.theme.mainColor};
                border-left-color: ${(props) => props.theme.mainColor};
                transition: border-color 0s ease-out 0.3s, width 0.15s ease-out 0.3s, height 0.15s ease-out 0.45s;
            }
        }
    }
`;

export default SkillsWrap;

 

 

 

 

 

 

 

import React from "react";
import styled from "styled-components";

function PortfolioMini() {
    const nodes = [].slice.call(document.querySelectorAll("li"), 0);
    const directions = { 0: "top", 1: "right", 2: "bottom", 3: "left" };
    const classNames = ["in", "out"].map((p) => Object.values(directions).map((d) => `${p}-${d}`)).reduce((a, b) => a.concat(b));

    const getDirectionKey = (ev, node) => {
        const { width, height, top, left } = node.getBoundingClientRect();
        const l = ev.pageX - (left + window.pageXOffset);
        const t = ev.pageY - (top + window.pageYOffset);
        const x = l - (width / 2) * (width > height ? height / width : 1);
        const y = t - (height / 2) * (height > width ? width / height : 1);
        return Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4;
    };

    class Item {
        constructor(element) {
            this.element = element;
            this.element.addEventListener("mouseover", (ev) => this.update(ev, "in"));
            this.element.addEventListener("mouseout", (ev) => this.update(ev, "out"));
        }

        update(ev, prefix) {
            this.element.classList.remove(...classNames);
            this.element.classList.add(`${prefix}-${directions[getDirectionKey(ev, this.element)]}`);
        }
    }

    nodes.forEach((node) => new Item(node));

    return (
        <StyledList>
            <h2>ETC</h2>
            <ul>
                <li>
                    <a href="http://coner.co.kr/project/samsung_galaxy/friday/" target="_blank" rel="noopener noreferrer">
                        <i className="far fa-file-alt"></i>
                        <p>Samsung Galaxy-Watch, Buds</p>
                    </a>
                </li>
                <li>
                    <a href="http://coner.co.kr/project/kia_sp2/unveiling/pc/index.html" target="_blank" rel="noopener noreferrer">
                        <i className="far fa-file-alt"></i>
                        <p>Kia Seltos Unveiling</p>
                    </a>
                </li>
                <li>
                    <a href="http://coner.co.kr/project/samsungpd/pd/nt930xbe-k38l/" target="_blank" rel="noopener noreferrer">
                        <i className="far fa-file-alt"></i>
                        <p>Samsung Notebook</p>
                    </a>
                </li>
                <li>
                    <a href="http://coner.co.kr/project/samsung_builtin/partners/" target="_blank" rel="noopener noreferrer">
                        <i className="far fa-file-alt"></i>
                        <p>Samsung Built-in Renewal</p>
                    </a>
                </li>
                <li>
                    <a href="http://coner.co.kr/project/samsungpd/pd/dv14n8520kv/" target="_blank" rel="noopener noreferrer">
                        <i className="far fa-file-alt"></i>
                        <p>Samsung 건조기 그랑데</p>
                    </a>
                </li>
                <li>
                    <a href="http://nolsoop.com/" target="_blank" rel="noopener noreferrer">
                        <i className="far fa-file-alt"></i>
                        <p>놀숲</p>
                    </a>
                </li>
                <li>
                    <a href="http://www.dselaser.com/ko/" target="_blank" rel="noopener noreferrer">
                        <i className="far fa-file-alt"></i>
                        <p>대신 엔터프라이즈</p>
                    </a>
                </li>
                <li>
                    <a href="http://www.ankugenc.com/" target="_blank" rel="noopener noreferrer">
                        <i className="far fa-file-alt"></i>
                        <p>안국이앤씨</p>
                    </a>
                </li>
                <li>
                    <i className="far fa-file-alt"></i>
                    <p>동아 도덕</p>
                </li>
                <li>
                    <i className="far fa-file-alt"></i>
                    <p>천재 통합사회</p>
                </li>
                <li>
                    <i className="far fa-file-alt"></i>
                    <p>지학사 영어 중등, 고등</p>
                </li>
                <li>
                    <i className="far fa-file-alt"></i>
                    <p>지학사 영어 공통</p>
                </li>
            </ul>
        </StyledList>
    );
}

export default PortfolioMini;

const StyledList = styled.div`
    max-width: 1140px;
    margin: auto;
    padding-bottom: 80px;
    h2 {
        padding-top: 20px;
        margin-bottom: 40px;
        font-size: 26px;
        font-weight: 600;
        text-align: center;
        position: relative;
        &::after {
            content: "";
            position: absolute;
            top: 0px;
            left: 0px;
            right: 0px;
            margin: auto;
            width: 500px;
            height: 1px;
            background-color: rgb(37, 37, 37);
        }
    }
    ul {
        display: flex;
        flex-wrap: wrap;
        justify-content: flex-end;
        width: 80%;
        margin: auto;
        li {
            width: 30%;
            margin-bottom: 4px;

            i {
                margin-right: 8px;
                vertical-align: top;
                line-height: 26px;
                color: ${(props) => props.theme.mainColor};
            }
            p {
                display: inline-block;
            }
        }
    }
    @media ${(props) => props.theme.laptop} {
        ul {
            li {
                width: 45%;
            }
        }
    }
    @media ${(props) => props.theme.mobile} {
        h2 {
            &::after {
                width: 70%;
            }
        }
        ul {
            width: 75%;
            li {
                width: 100%;
            }
        }
    }
`;

 

 

이 코드는 React 컴포넌트인 PortfolioMini를 정의하고, 이 컴포넌트가 포트폴리오 항목을 나타내는 목록을 렌더링하는 역할을 합니다. 이 코드는 React 컴포넌트와 CSS 스타일을 결합하여 포트폴리오 항목을 표시하는 데 사용됩니다. 이 코드를 간단히 해석하겠습니다.

PortfolioMini 컴포넌트는 StyledList 컴포넌트를 반환합니다.

StyledList 컴포넌트는 CSS 스타일이 적용된 <div> 엘리먼트를 나타냅니다. 이 엘리먼트는 포트폴리오 항목 목록을 감싸는 컨테이너로 사용됩니다.

<h2> 엘리먼트는 "ETC"라는 텍스트를 포함하며, 포트폴리오 항목 목록의 제목을 나타냅니다. 이 제목은 가운데 정렬되어 있으며 하단에 1픽셀 높이의 수평 선이 있는 스타일을 가지고 있습니다.

<ul> 엘리먼트는 포트폴리오 항목의 목록을 나타냅니다. 이 목록은 가로로 표시되며, 화면 너비의 80%를 차지하며 가운데 정렬됩니다.

각 포트폴리오 항목은 <li> 엘리먼트 내부에서 구성됩니다. 각 항목은 가로 너비의 30%를 차지하며 간격을 가집니다.

각 포트폴리오 항목은 아이콘과 텍스트 링크를 포함하고 있습니다. <i> 엘리먼트는 아이콘을 나타내며, <p> 엘리먼트는 포트폴리오 항목의 이름 또는 설명을 표시합니다.

미디어 쿼리 (@media)를 사용하여 화면 크기에 따라 스타일이 다르게 적용됩니다. 예를 들어, 데스크톱 화면에서는 항목의 가로 너비가 30%로 설정되고, 모바일 화면에서는 가로 너비가 100%로 설정됩니다.

이 코드는 React와 styled-components를 사용하여 포트폴리오 항목 목록을 생성하고 스타일을 적용하는 예시입니다. 이 코드를 통해 포트폴리오 목록을 만들고 다양한 화면 크기에 대응하는 반응형 디자인을 구현할 수 있습니다.

 

 

 


이 코드는 CSS의 미디어 쿼리를 사용하여 화면 크기가 모바일 디바이스에 적용되는 스타일 규칙을 정의합니다. 아래에서 각 부분을 자세히 해석하겠습니다.

  1. @media ${(props) => props.theme.mobile}: 이 부분은 미디어 쿼리를 시작하는 부분입니다. 이 미디어 쿼리는 props.theme.mobile 조건이 참인 경우에만 실행됩니다. 이것은 주로 React 컴포넌트의 props 중에 theme이라는 속성을 통해 화면 크기에 대한 정보를 전달하는 방식으로 사용됩니다.
  2. h2 { ... }: 이 부분은 미디어 쿼리 내부에서 h2 태그에 적용되는 스타일을 정의합니다. 즉, 화면 크기가 모바일 디바이스일 때 h2 태그에 스타일을 적용합니다.
  3. &::after { ... }: h2 태그 내부에 있는 ::after 가상 요소에 대한 스타일을 정의합니다. ::after는 요소 내의 가상으로 생성된 콘텐츠를 스타일링하는 데 사용됩니다.
  4. width: 70%;: 모바일 화면에서 h2 태그의 가상 요소인 ::after의 너비를 70%로 설정합니다. 이로써 가상 요소의 가로길이가 h2 태그의 가로길이의 70%가 됩니다.
  5. ul { ... }: 미디어 쿼리 내부에서 ul 태그에 적용되는 스타일을 정의합니다.
  6. width: 75%;: 모바일 화면에서 ul 태그의 가로길이를 75%로 설정합니다. 이로써 목록의 가로길이가 화면의 75%가 됩니다.
  7. li { ... }: 미디어 쿼리 내부에서 li 태그에 적용되는 스타일을 정의합니다.
  8. width: 100%;: 모바일 화면에서 li 태그의 가로길이를 100%로 설정합니다. 이로써 각 항목이 목록의 전체 가로 공간을 차지하게 됩니다.

이렇게 설정된 미디어 쿼리는 화면 크기가 모바일 디바이스인 경우에만 적용되며, 해당 화면 크기에서는 h2, ul, li 태그의 스타일이 변경됩니다.






반응형