[Clone Project-5] 채용 리스트 구현

시작하며 🎉

힘들었던 carousel 구현을 끝내고 나서,
뭔가 점차 고민을 하다 보니, 좀 더 구현에 있어 자신감이 생긴 듯합니다. 👏
지금 다룰 채용 리스트 컴포넌트 역시, 집중 빡!하고 끝내서 기분이 좋네요. 😘

그렇게 어려운 건 없었지만 이 프로그래머스 페이지 자체가 breakpoint가 4개인지라... 시간은 좀 걸리지만, 그래도 좋은 경험을 하는 거서 같아 기분이 좋습니다.

그럼 시작합니다!!

본론 📃

제가 클론할 job은 말이죠, 다음과 같이 구성되어 있어요.
데스크탑에서는(992px~) 다음과 같이 렌더링되는데요,

991px 부터는 이제 한 줄당 하나씩 나오게 됩니다.

그리고 575~766px에서는 그냥 글자만 작아지는데요,
모바일(574px)에서는 로고가 보이지 않게 됩니다.

index.html

저는 다음과 같이 구성했습니다!
먼저 이 역시 독립적으로 구성될 수 있는 콘텐츠라 생각해서 article로 시멘틱하게 해줬어요.

또한 이 콘텐츠가 어떤 페이지인지 알려주는 헤더는 header로 마크업 했습니다.

이후 skill들과 카드들은 ul태그로 썼고, 나머지 상세 하위 요소들은 li 태그로 감싸주었어요.

    <article class="job">
        <header class="job__header">
            <h2 class="job__title">채용 중인 포지션</h2>
            <button class="job__more-btn">포지션 더보기</button>
        </header>
        <!-- 
        <ul class="job__skills">
            <li class="job__skill">Java</li>
            <li class="job__skill">Spring</li>
            <li class="job__skill">Node.js</li>
            <li class="job__skill">Django</li>
            <li class="job__skill">ReactJS</li>
            <li class="job__skill">Vue.js</li>
            <li class="job__skill">JavaScript</li>
            <li class="job__skill">Python</li>
            <li class="job__skill">Kotlin</li>
            <li class="job__skill">C++</li>
            <li class="job__skill">Android</li>
            <li class="job__skill">IOS</li>
            <li class="job__skill">서버/백엔드</li>
            <li class="job__skill">프론트엔드</li>
            <li class="job__skill">웹 풀스택</li>
            <li class="job__skill">안드로이드 앱</li>
            <li class="job__skill">아이폰 앱</li>
        </ul>
        <ul class="job__cards">
            <li class="job__card">
                <div class="job__logo-box">
                    <img src="https://grepp-programmers.s3.amazonaws.com/production/company/logo/1718/20160804_%EB%A1%9C%EA%B3%A0%EC%84%B8%EB%A1%9C%ED%98%95.png" alt="채용회사 로고 이미지" class="job__card-logo">
                </div>
                <section class="job__card-info">
                    <h4 class="job__card-title">웹 서비스 개발자</h4>
                    <h4 class="job__card-brand">오니온파이브</h4>
                    <ul class="job__requirements">
                        <li class="job__requirement">웹 풀스택</li>
                        <li class="job__requirement">Django Channels</li>
                        <li class="job__requirement">Celery</li>
                        <li class="job__requirement">Django</li>
                        <li class="job__requirement">Redis</li>
                        <li class="job__requirement">MariaDB</li>
                    </ul>
                </section>
            </li>
            <li class="job__card">
                <div class="job__logo-box">
                    <img src="https://grepp-programmers.s3.amazonaws.com/production/company/logo/1718/20160804_%EB%A1%9C%EA%B3%A0%EC%84%B8%EB%A1%9C%ED%98%95.png" alt="채용회사 로고 이미지" class="job__card-logo">
                </div>
                <section class="job__card-info">
                    <h4 class="job__card-title">웹 서비스 개발자</h4>
                    <h4 class="job__card-brand">오니온파이브</h4>
                    <ul class="job__requirements">
                        <li class="job__requirement">웹 풀스택</li>
                        <li class="job__requirement">Django Channels</li>
                        <li class="job__requirement">Celery</li>
                        <li class="job__requirement">Django</li>
                        <li class="job__requirement">Redis</li>
                        <li class="job__requirement">MariaDB</li>
                    </ul>
                </section>
            </li>
        </ul> -->
    </article>

Job.ts

이번에는 뭔가 IIFE를 써보고 싶었어요!
써본 결과, 뭔가 개인적으로는 좀 더 이게 편하고, 직관적이라는 생각이 들었습니다.

바로 실행한다는 의미가 있어서일까요? 여기서 쓰면 바로 돌아가겠다는 느낌이 들어서, 위치를 고민할 필요가 없어서 좋았습니다.
(실제로는 정반대일 수도 있고, 이렇게 쓰이지는 않을 수 있겠지만요.)

또, 커스텀을 해봤는데요, 여러 개를 계속 똑같이 createElementappendChild하기 귀찮아서 rest연산자 등등을 활용하면서 구현해봤어요!

기존 함수들과 헷갈리지 않게 custom을 변수명 맨 앞에 붙여줬습니다.

interface jobDataFormat {
    name: string;
    url: string;
    image: string;
    jobName: string;
    requirements: Array<string>;
}

interface Names {
    [name: string]: string;
}
interface SkillDatasFormat {
    0: {
        skills: Array<string>;
    };
};

export default class Job {
    private readonly names: Names;
    constructor(private readonly jobDatas: Array<jobDataFormat>, private readonly SkillDatas: SkillDatasFormat) {
        this.names = {
            jobHeader: 'job__header',
            jobTitle: 'job__title',
            jobMoreBtn: 'job__more-btn',
            jobSkills: 'job__skills',
            jobSkill: 'job__skill',
            jobCards: 'job__cards',
            jobCard: 'job__card',
            jobLogoBox: 'job__logo-box',
            jobCardLogo: 'job__card-logo',
            jobCardInfo: 'job__card-info',
            jobCardTitle: 'job__card-title',
            jobCardBrand: 'job__card-brand',
            jobRequirements: 'job__requirements',
            jobRequirement: 'job__requirement'
        }
        this.render();
    }
    customCreateElement(tag: string, name: string): HTMLElement {
        const elem = document.createElement(tag);
        elem.className = name;
        return elem;
    };
    customAppendChild(parent: HTMLElement, ...elems: Array<HTMLElement>) {
        [...elems].map(elem => parent.appendChild(elem));
    };
    render() {
        const renderSkillsComponent = (() => {
            const $jobSkills = this.customCreateElement('ul', this.names.jobSkills);
            const datas = this.SkillDatas[0].skills;
            datas.map(data => {
                const $jobSkill = this.customCreateElement('li', this.names.jobSkill);
                $jobSkill.textContent = data;
                $jobSkills.appendChild($jobSkill)
            });
            document.querySelector('.job').appendChild($jobSkills);
        })();

        const renderCardsComponent = (() => {
            const $jobCards = this.customCreateElement('ul',this.names.jobCards);
            this.jobDatas.map((jobData: jobDataFormat) => {
                const $jobCard = this.customCreateElement('li',this.names.jobCard);

                const $jobLogoBox = this.customCreateElement('div',this.names.jobLogoBox);
                const $jobCardLogo = this.customCreateElement('img', this.names.jobCardLogo);
                $jobCardLogo.setAttribute('src', jobData.image);
                $jobCardLogo.setAttribute('alt', "채용회사 로고 이미지");

                // job__logo-box 생성
                $jobLogoBox.appendChild($jobCardLogo);

                const $jobCardInfo = this.customCreateElement('section', this.names.jobCardInfo);
                const $jobCardTitle = this.customCreateElement('h4', this.names.jobCardTitle);
                $jobCardTitle.textContent = jobData.jobName;
                const $jobCardBrand = this.customCreateElement('h4', this.names.jobCardBrand);
                $jobCardBrand.textContent = jobData.name;
                const $jobRequirements = this.customCreateElement('ul', this.names.jobRequirements);
                jobData.requirements.map((requirement: string) => {
                    const $jobRequirement = this.customCreateElement('li', this.names.jobRequirement);
                    $jobRequirement.textContent = requirement;
                    $jobRequirements.appendChild($jobRequirement);
                })

                // job__card-info 생성
                this.customAppendChild($jobCardInfo, $jobCardTitle, $jobCardBrand, $jobRequirements);

                // 결과적으로 job__card 생성
                this.customAppendChild($jobCard, $jobLogoBox, $jobCardInfo)
                
                // job__card를 job__cards 안에 넣음
                $jobCards.appendChild($jobCard);
            })
            document.querySelector('.job').appendChild($jobCards)
        })();
    }
}

결과

아주 어썸하게 잘 나왔네요! 🌈🎉

마치며 😍

원래는 리액트로 하다가, 자바스크립트로 하니 뭔가 좀 생산성에 있어 불편한 점은 있지만,

아, 원래 이렇게 자바스크립트로 작업했었구나. 그래서 리액트, 뷰같은 라이브러리 및 프레임워크가 생산성 측면에서 이점이 있구나를 느낍니다.

클론 프로젝트 정말 매력 있는 것 같아요. 앞으로도 자주 해야겠습니다!!

💬
아직 많이 부족하기 때문에 잘못된 생각이 많을 수 있습니다. 틀린 부분이 있다면 날카로운 비판 매우 환영합니다!! 읽어주셔서 감사합니다 🎉

좋은 웹페이지 즐겨찾기