반응형

How To Eat

우리 매장은 맛있게 먹는 방법이 있다. 깻잎에 마요네즈가 섞인 천사채와 날치알, 환상의 마요네즈 소스까지 곁들여 쭈꾸미와 함께 쏙하면 기가막힌 천상의 맛이다. 하지만, 외국인들에게 이 맛있는 조합을 소개하기가 매우 어려운 사실.. 이를 해결하기 위해 만들어 보도록 하겠다.

 

이전 시간에 사용한 모달을 재활용해서 How To Eat 버튼과 모달창을 구현해보겠다.

 

const HowtoeatButton = () => {
    const [isModalOpen, setIsModalOpen] = useState(false);
    const [curPage, setCurPage] = useState(0);

    const navigatePage = (direction : Direction) => {
        setCurPage((prevPage) => {
            if (direction === 'prev' && prevPage > 0) {
                return prevPage - 1;
            } else if (direction === 'next' && prevPage < MAX_PAGE) {
                return prevPage + 1;
            }
            return prevPage;
        });
    };

    return (
        <div style={{ position: "fixed", bottom: "20px", zIndex: 1000 }} className="left-1/2 how-to-eat">
            <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" onClick={() => setIsModalOpen(true)}>How To Eat</button>
            {isModalOpen && (
                <HowtoeatModal open={isModalOpen} onClose={() => setIsModalOpen(false)}>
                    <div className="flex h-11/12 bg-blue-700">
                        <NavigationButton direction="prev" onClick={() => navigatePage('prev')} />
                        <ContentDisplay img={img} description={desc[curPage]} pageNumber={curPage} />
                        <NavigationButton direction="next" onClick={() => navigatePage('next')} />
                    </div>
                </HowtoeatModal>
            )}
        </div>
    );
};

 

여기에서 두가지 버튼은 모달의 이미지를 넘기는 왼쪽, 오른쪽 버튼이다.

 

const navigatePage = (direction : Direction) => {
        setCurPage((prevPage) => {
            if (direction === 'prev' && prevPage > 0) {
                return prevPage - 1;
            } else if (direction === 'next' && prevPage < MAX_PAGE) {
                return prevPage + 1;
            }
            return prevPage;
        });
    };

 

위 함수를 통해 방향이 무엇인지에 따라 누른 버튼을 확인하고, 방향에 따른 페이지수 변화를 주는 역할이다.

대충 출력되는 화면을 확인해보자면

 

 

으 정말 멋없는 디자인.. 위의 색들은 화면 크기 조정을 위해 프레임을 만들어둔 것 이므로 뭐라하지 마세요!!

이제 여기에 찍어둔 사진을 적용해보겠다.

 

는 실패 (* 아직 사진 최신화가 되지 않아 불가능 ㅠㅠ)

 

사진 적용은 나중으로 미루고 디자인을 수정해야겠다. 기존 버튼 배치가 불편해보여서 하단으로 이동했다. 그리고 이미지와 컨텐츠 높이를 반반으로 수정했다.

 

 

 

 

 

 

무이스!!!! 확실히 이전보다 나아진 느낌!! 디자인 전공자 동생왈 매장의 메인 색 테마를 정하고 칠해라. 그리하여 색깔을 셀렉

해보았다. 전체적인 색 조합을 고려해보려고 이것저것 뒤져보았다.

 

 

요놈들이 뭔가 열정도 쭈꾸미의 유니폼 '검정' 과 열정도의 '빨강', 쭈꾸미의 '선홍' 이 어울리는 듯한 뭔가 그럴싸한 느낌을 받아서 셀렉!

 

 

확실히 디자인이 사는구만!! 좋습니다. 이거지 이거야. 이제 사진만 최신화하면 완성될 맛있게 먹는법 완성!

반응형
반응형

Use Case

  1. 메뉴 출력을 N x 2 로 만들고 메뉴 마다 디테일 모달 띄우기
  2. Sticky Navbar 제작
  3. How to Eat

디테일 모달

현재 메뉴판은 한 줄로 쭉 나열되어있고 메뉴 카드마다 메뉴명, 메뉴 한국어명, 메뉴 디테일, 가격 이렇게 정보가 한꺼번에 나와있다. 내 생각에는 정보가 한 번에 다 나오면 귀찮은 동작없이 메뉴를 살필 수 있다고 생각하는데 아무래도 메뉴가 하나씩 나오게 되면 사용자 입장에서는 더 많은 스크롤을 내려야 한다. 따라서, N * 2 로 메뉴 리스트를 바꾸고 각 메뉴를 눌렀을 때 디테일 모달이 나오도록 변경하려고 한다.

우선, 모달 띄우기 전 화면에 출력할 내용은 외국어 메뉴명, 한국어 메뉴명, 가격이다. 이후 각 메뉴의 Element 에 onClick 을 부여해 데이터를 모달에 디테일 한 작업을 추가할 예정이다. 추후에 디테일 모달에는 Carousel 을 넣고 싶다.

 

N x 2의 메뉴

 

.menu-list {
    @apply flex flex-wrap gap-4 justify-center items-center;
}

@media (max-width: 600px) {
    .menu-list {
        @apply flex-row;
    }
}

 

gap 클래스를 기호에 맞게 수정해 주어 카드간의 간격을 조정하고, .menu-list 클래스에 flex-row로 변경하여 카드가 열별로 출력되게 변경했다.

 

이제 디테일 모달을 만들차례

 

만들려고 시도를 하자마자 클릭 이벤트를 넣는데 문제가 발생했다.

어이없는 오류 1 : 부모 Div 안에 모달 넣지말자

문제 상황

처음에는 모달을 열고 닫는 간단한 기능을 구현하려고 했다. MenuItemCard 컴포넌트에서 카드 요소를 클릭하면 모달이 열리고, 모달 내부의 닫기 버튼을 클릭하면 모달이 닫히도록 했다. 코드의 핵심 부분은 다음과 같다.

<div className="rounded-lg shadow-md bg-white overflow-hidden w-72 h-150 card" onClick={handleModalOpen}> 
    <Image src={imageUrl} alt={name} className="object-cover w-full h-1/2" width={240} height={100} /> 
    <div className="p-4 h-1/2"> 
        <h3 className="text-xl font-extrabold mb-2">{name}</h3> 
        <div className="text-md font-normal">{discription}</div> 
        <div className="text-gray-700 card-price">{price}</div> 
    </div> 
    {isModalOpen && ( 
        <DetailModal open={isModalOpen} onClose={handleModalClose}> 
            <div className="flex h-11/12 bg-blue-700"> {/* 모달 내용 */} </div> </DetailModal> )} 
</div>

 

이렇게 보면 당연히! 동작되었을 줄 알았지만 모달창이 영원히 닫히지 않는 문제가 생기게 되었다. 도대체 무엇이 문제였을까?!

 

문제 원인

문제의 원인은 모달이 부모 divonClick 이벤트에 의해 닫히는 것이었다. 모달을 클릭할 때도 부모 divonClick 이벤트가 호출되면서 setIsModalOpen 함수가 번복되는 상황이 발생했다.

 

해결 방법

해결 방법은 간단했다. onClick 이벤트를 부모 div에서 제거하고, 필요한 자식 요소에만 이벤트 핸들러를 설정하면 된다.

 

이 따위 문제를 2시간 넘게 잡고 있다는 것. 개발자로 살아남아가고 있다는 것.

 

수정된 코드
<div className="rounded-lg shadow-md bg-white overflow-hidden w-72 h-150 card">
            <div onClick={handleModalOpen}>
                <Image
                    src={imageUrl}
                    alt={name}
                    className="object-cover w-full h-1/2"
                    width={240}
                    height={100}
                />
                <div className="p-4 h-1/2"  >
                    <h3 className="text-xl font-extrabold mb-2">{name}</h3>
                    <div className="text-md font-normal">{discription}</div>
                    <div className="text-gray-700 card-price">{price}</div>
                </div>
            </div>

            {isModalOpen && (
                <DetailModal open={isModalOpen} onClose={()=>handleModalClose()}>
                    <div className="flex h-11/12 bg-blue-700">
                        {/* 모달 내용 */}
                    </div>
                </DetailModal>
            )}
        </div>

 

결과물

 

 

한국어 메뉴일 때는 공백이 매우 신경쓰이는데 일단 디자인 적인 부분은 나중에 신경쓰기로 했다. 깨알 수정안한 맛있게먹는법의 흔적

 

다음에는 How To Eat 모달을 수정할 예정!

반응형
반응형

배경

계절학기에 들어가면서 1학점 짜리 웹 프로젝트 연구를 진행하게 되었다. 주제는 웹 보안을 위한 시큐어 코딩 학습 사이트다.

3주 동안 진행되는 계절학기 특성상 미리 준비하는게 정신건강에 좋기 때문에 미리 웹사이트를 구축해보기로 했다.

 

새로운 기술

우선, 평소에는 react, nodejs 로 웹을 구축했었는데 이번에는 nextjs 를 이용해 구축해보려고 한다. 새로운 기술에 대해 배우는고 공부하는데 도움이 될 것 같아 이렇게 진행하려고 한다.

 

취약점 조사

프로젝트에는 웹보안 하면 가장 기초가되는 4가지 공격을 제외한 공격을 다루라고 명시되어 있다. (sql injection, XSS, path traversal, desdrialization)

사실 내가 생각해도 위의 공격은 너무 소스도 많고 기본적인 문제들이라 날로 먹는 느낌이 들 것 같긴했다.

그렇기에 새로운 취약점을 찾아보았고, OWASP 에서 발표한 TOP10 취약점을 알게 되었다.

 

 

여기서 나는 1번 injection, 8번 CSRF 에 대해 진행해보려고 한다. injection 에도 file upload, os command 를 이용한 취약점에 집중하려고 한다.

 

웹 구현

 

next 프로젝트를 구축한 후 app 폴더의 트리 모습이다. layout.js 를 이용해 헤더와 네비게이션을 고정해놓고 page를 바꾸어가며 출력할 예정이다.

 

app
│ favicon.ico
│ globals.css
│ layout.js
│ page.js

├─components
│ Header.js
│ Nav.js

├─csrf
│ page.js

├─file
│ page.js

├─list
│ page.js

└─os

 

헤더와 네비게이션을 고정하기 위해 다음과 같은 코드를 작성했다.

 

layout.js

import Link from "next/link";
import "./globals.css";
import { Inter } from "next/font/google";
import Nav from "./components/Nav";
import Header from "./components/Header";

const inter = Inter({ subsets: ["latin"] });

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Header/>
        <Nav />
        {children}
      </body>
    </html>
  );
}

Header.js

'use client';
const { default: Link } = require("next/link");

const Header = () =>{
  const toggleDropdown = () => {
    setDropdownVisible(!isDropdownVisible);
  };
    return(
        <div className="header">
            <h3>Secure Coding Lecture</h3>
        </div>
    )
}

export default Header;

Nav.js

'use client';
import { useState } from "react";
const { default: Link } = require("next/link");

const Nav = () =>{
    const [isDropdownVisible, setDropdownVisible] = useState(false);

  const toggleDropdown = () => {
    setDropdownVisible(!isDropdownVisible);
  };
    return(
        <nav className="navbar">
          <ul className="navList">
            <li className="navItem">
              <Link href="/">홈</Link>
            </li>
            <li className="navItem">
              <div onClick={toggleDropdown}>강의
              {isDropdownVisible && (
                <ul className="subNavList">
                  <li className="navItem">
                    <Link href="/file">File Upload Attack</Link>
                  </li>
                  <li className="navItem">
                    <Link href="/os">OS Command Injection</Link>
                  </li>
                  <li className="navItem">
                    <Link href="/csrf">CSRF</Link>
                  </li>
                </ul>
              )}
              </div>
            </li>
            <li className="navItem">
              <Link href="/">설정</Link>
            </li>
          </ul>
        </nav>
    )
}

export default Nav;

위의 코드를 이용하면 다음과 같은 웹이 출력된다.

 

 

 

어려웠던점

  1. Layout.js 내에서 useState 사용불가
    • 이건 내가 next 에 대해 제대로 몰라 생겼던 문제다. next 는 기본적으로 서버 컴포넌트로 작동하는데 useState 는 클라이언트에서만 사용되기에 오류가 발생한다.
    • 따라서, use client 를 최상단에 적어 클라이언트 임을 선언하면 정상작동 된다.
반응형

+ Recent posts