프론트엔드 개발자들

프론트엔드 개발자들

프론트엔드 개발자들을 위한 커뮤니티 클럽입니다.

공개 178 멤버

가이드라인

["개인 또는 단체, 속한 조직을 비방하는 글을 '금지'합니다.","타인의 글을 공유할 땐 왜 공유하려고 하는지 이유도 함께 적어주면 좋습니다.","\b작성한 글을 공유할 땐 어떤 내용인지 간단하게 요약해주면 좋습니다.","기술 관련 질문을 올릴 땐 '문제 상황', '시도한 방법' 등을 적어서 답변하는 분에게 충분한 정보를 제공해줍니다.","조직 문화 등 비기술 관련 질문을 올릴 땐 정확히 어떤 내용이 궁금한지 '명확'하게 '요약'되어 있으면 좋습니다."]

준프

준프

JSON은 정말 느립니다. 더 빠른 대안을 살펴봐요 !

요즘 Medium에서 핫한 글 하나 소개하려고 합니다. 이 글은 아래와 같은 내용을 담고 있습니다.

  1. JSON을 왜 사용하는지

  2. 앱에서 속도와 반응이 왜 중요한지

  3. JSON이 앱을 느리게 하는지?

  4. 왜 JSON이 앱을 느리게 하는지

  5. JSON의 대안

  6. 데이터 포맷 최적화 (왜 바이트 단위가 중요한지)

  7. 바이너리 포맷을 통한 사이즈 효율화

  8. JSON 퍼포먼스 최적화

  9. 실제 사례를 통해 살펴보는 JSON 속도 최적화

  10. 결론

이 글은 JSON을 왜 많이 사용하는지, 그리고 어떤 점이 문제인지 살펴봅니다. 사실 이 부분을 살펴보며 이런 생각이 들었습니다.

대부분의 서비스는 JSON 최적화를 위해 투입하는 비용 대비 효용이 좋지 않은 것 같다. 확실히 JSON의 장점이 뚜렷하다.

그만큼 JSON이 왜 널리 사용되고 어떤 장점이 있는지도 분명하게 제시하고 있습니다.

하지만 그만큼 인사이트도 많은데요, 인상 깊었던 세부 주제를 살펴보면

  1. JSON은 범용성 측면에서 확실한 장점이 있습니다.

  2. JSON은 serialize하고 deserialize하는 비용이 큰 편인데, 마이크로 서비스의 경우 특히 비용이 높아질 수 있습니다.

  3. JSON의 대체제가 있다니!

  4. JSON을 잘 사용하는 방법이 있다니!

  5. JSON을 사용했을 때 발생한 이슈를 해결한 사례들이 너무 좋습니다.

와 같습니다.

더 자세한 내용은 원문을 읽어보시면 좋을거 같아요 !

0
4
조홍비

조홍비

커뮤니케이션을 잘 하는 개발자란?

흔히들 커뮤니케이션 역량이 중요하다고들 하는데요.

커뮤니케이션 역량이 뛰어나다는 건 구체적으로 어떤 의미인지 궁금하지 않으셨나요?

제가 읽었던 글 중에 좋은 글이 있어서 소개해보려 합니다.

글의 내용을 요약하면 이렇습니다.

커뮤니케이션을 잘 하는 개발자는 문제 해결에 집중한다.
'안 된다'고 말하지 않고, 그 요구사항이 해결하려는 문제가 무엇인지 파악한 뒤 문제를 해결할 수 있는 다른 방법을 제안한다.

더불어서 글의 내용과는 상관이 없지만 글쓴이님(Eddy님)의 태도 또한 인상깊었는데요!

기자 출신의 개발자인 Eddy님은 궁금한 점(가령 이번 주제처럼 커뮤니케이션을 잘 하는 개발자란 무엇일까? 같은)이 생겼을 때 회사 동료 분들께 적극적으로 질문하며 스스로 답을 찾아가시더라고요. 🙂

일을 하면서 참 다양한 고민과 걱정들이 생기는데 이렇게 주변 사람들에게, 또는 나에게 질문을 던지면서 답을 찾아가는 과정이 정말 좋다고 생각합니다. 질문을 던지는 행위 자체도 좋은 커뮤니케이션 수단인 것 같아요. 😄

전체 글 >>

0
2
준프

준프

재사용 가능한 컴포넌트를 그만 만드세요

제목부터 자극적인 이 글은 생각보다 정말 순합니다. 우리가 리액트 컴포넌트를 만들면서 마주하는 현실을 있는 그대로 적고 있습니다.

저자는 재사용 가능한 컴포넌트를 만들면서 일종의 반복적인 패턴에 빠졌다고 합니다. 그리고 만족스러웠습니다.

After following a very similar pattern and feeling good about myself, I started wiring up my page.

하지만 재사용을 고려하며 만든 컴포넌트는 재사용되지 않았고 재사용을 고려하다보니 코드는 복잡해지기만 했다고 합니다.

I hadn’t written any of my components based around data.

(그 어떤 데이터도 데이터를 기반으로 작성하지 않았습니다.)

...

Many of those “reusable” components were never reused in the app and many, including my Hero component, never even needed dynamic data.

('재사용 가능한' 대다수의 컴포넌트는 절대 재사용되지 않았고, 심지어 동적 데이터도 필요하지 않았습니다.)

그리고 저자는 재사용 가능한 코드를 작성하는 건 '미래의 가정된 문제를 해결하면서 지금 마주한 어려운 문제를 해결하기를 미루는 것', 즉 일종의 '미루기(procrastination)'라고 봤습니다.

... we humans tend to write code like this as a form of procrastination. Delaying solving the hard problems we have now, by solving the hypothetical problems of the future.

그리고 저자는 재사용 가능한 코드를 작성하고 있다면 '내가 지금 해결해야 하는 문제인지 정직하게 살펴야 합니다.'라고 전합니다.

'오브젝트'의 저자 조영호님도 책에 아래와 같은 말을 남기고 있습니다.

변경은 예상이 아니라 현실이어야 한다. 미래에 변경이 일어날지도 모른다는 막연한 불안감은 불필요하게 복잡한 설계를 낳는다. 아직 일어나지 않은 변경은 변경이 아니다. <오브젝트>, 조영호, 305쪽

더 자세한 내용은 원문을 참고해주세요 :)

0
7
문지웅

문지웅

섬네일 이미지를 자동으로 생성하기 (with @vercel/og)

블로그에 글을 작성할 때, 섬네일(thumbnail)을 매번 직접 만드는 것은 추가적인 비용이 필요한 작업입니다. 이를 해결하기 위해 미니픽(https://mini-pick.vercel.app)을 직접 만들었지만, 제목 등을 직접 입력해야 해서, 자동화하면 어떨까 하는 needs가 생겼습니다.

`@vercel/og` 라이브러리를 도입해서 자동으로 이미지를 생성하도록 해서 이를 해결할 수 있었습니다.

※ Next.js 13 + App Router 버전 기준입니다.

공식 문서를 참고해서, app 디렉토리에 og 폴더를 만들고, route.tsx 파일을 생성하고, 아래와 같이 구현했습니다.

// app/og/route.tsx
import { ImageResponse } from 'next/server';

export const runtime = 'edge';

export async function GET(request: Request) {
  try {
    const { searchParams } = new URL(request.url);

    const hasTitle = searchParams.has('title');
    const title = hasTitle
      ? searchParams.get('title')?.slice(0, 100)
      : 'Woongsnote';

    const size = { width: 1200, height: 630 };

    return new ImageResponse(
      (
        <div
          style={{
            height: '100%',
            width: '100%',
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            justifyContent: 'center',
            backgroundImage:
              'linear-gradient(to bottom right, #00C0FF, #4218B8)',
            fontSize: 64,
            fontWeight: 600,
          }}
        >
          <div style={{ color: 'white' }}>{title}</div>
        </div>
      ),
      {
        ...size,
      },
    );
  } catch (error) {
    return new Response(`Failed to generate the Image`, { status: 500 });
  }
}
```

Next.js의 Metadata API 기준으로 해당 이미지를 Open-graph image로 사용하려면, 아래와 같이 호출하면 됩니다.

    openGraph: {
      images: [
        {
          url: `/og?title=${post.title}`,
          width: 1200,
          height: 630,
          alt: post.title,
        },
      ],
    },

동일한 url로 섬네일로 사용하려고 했으나, 배포 환경에서 400 Error 를 만났습니다.

Next.js 의 Image를 사용하려다 보니, image에 대한 주소 설정이 안되어 있다고 생각되어 아래와 같이 config 파일에 remotepattern을 추가했습니다.

{
        protocol: 'https',
        hostname: '배포한 url 주소',
        port: '',
        pathname: '/og/**',
 },

또한, 이미지를 불러오는 url도 아래와 같이 수정했습니다.

src={`${배포한 url}/og?title=${title}`}

수정 후에 재배포한 결과, 정상적으로 동작하는 것을 확인할 수 있었습니다.

아래 블로그에서 실제로 생성된 섬네일을 확인할 수 있습니다.

감사합니다. 😊

0
2
심은광

심은광

Docusaurus로 기술문서 만들어보세요 :)

최근 모노레포를 도입하면서 유틸 함수, 훅스 등을 공용화 하는 작업을 진행했었는데

하는 김에 문서화도 같이 진행하면 좋겠다는 생각이 들어서 이것저것 알아보다가 Docusaurus를 알게 되었습니다.

사용해 보니 너무 편하고 좋은 것 같아서 추천드립니다 !!

Docusaurus는 Meta에서 제공하는 오픈소스 문서 생성 도구로, React 기반으로 작동합니다.

너무나 깔끔한 기본 템플릿을 제공해주고 있고, 몇가지 설정만 해주면 바로 문서작성을 시작할 수 있습니다.

저는 로고 변경, 컬러 수정 등의 최소한의 커스텀 작업만 하고 바로 문서화를 시작했는데, 결과물이 생각보다 만족스러워서 추가적으로 커스텀을 하지 않아도 될 것 같아요.

스크린샷 2023-10-16 오후 11.15.34.png

Docusaurus는 Meta에서 제공하는 오픈소스인 만큼 공식문서가 굉장히 친절하게 되어있어서,

공식 문서 보시면 쉽게 활용하실 수 있으실 겁니다!!

공식 문서 링크 : https://docusaurus.io/

기술문서를 만드는 데 디자인이 고민되시거나,

빠르게 문서작성을 하고 싶으신 분들이 활용하시면 좋을 것 같아요 :)

혹시 더 매력적인 문서작성 도구를 알고 계시는 분이 있다면 .. 저도 알려주세요!! 감사합니다!!

0
0
준프

준프

커링 패턴

커링 패턴을 아시나요? 자바스크립트에서 클래스를 사용하기 전부터 종종 사용하던 패턴 중 하나입니다. 몇몇 책에서 소개할 땐 아래와 같은 예시가 대다수였고 공감이 잘 안됐던 기억이 있습니다.

(패턴이란 '어떤 문제를 해결하는데 반복되는 방법을 공식화 한 것이다.'라고 생각하면 편합니다. 분명 기억해두면 활용할 곳이 나타날거라 생각합니다 :) )

const setAdd = (fixNumber) => {
  return (num) => {
    return fixNumber + num;
  };
};

const add5 = setAdd(5);

console.log(add5(10)); // 15
console.log(add5(20)); // 25

(도대체 이걸 어디에다 쓴다는거지...?)

하지만 어떤 개념이든 알아두고 문제를 해결할 때 '아, 그그 그거 있는데, 좋은거'하고 떠오른다면 충분하다고 생각합니다. 제게 커링 패턴이 그랬던 것 같습니다.

오늘 소개해드리는 글은 커링 패턴이 어떤 것이고 실제로 어떻게 사용하면 좋은지 유쾌하게 소개하고 있습니다.

https://blog.stackademic.com/spice-up-your-javascript-with-currying-894a7c463d03

그리고 최근에 제가 쓴 글에서도 간단하게 소개하고 있습니다.

https://medium.com/@shinbaek89/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%ED%95%A8%EC%88%98%EC%99%80-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0-50752df217b5

0
0
준프

준프

클린 단위 테스트

단위 테스트를 작성할 때 중요하게 생각하는 것 중 하나가 바로 '테스트 코드 역시 관리해야 하는 코드다'입니다. 테스트를 작성하고 유지하다보면 이 역시 만만찮은 비용이 들어간다는 사실을 알게됩니다. 나중에 유지하기 어려워서 테스트를 꺼버리는 경험을 누구나 한 번쯤은 하곤 하죠.

it.skip('...', () => {...}); // 이 테스트는 이제 더이상 유지할 수 없어...

그렇기 때문에 테스트 코드를 작성할 때에도 유지하기에 좋은 방법으로 작성해야 합니다. 이번에 소개해드릴 글은 테스트 코드를 어떻게 잘 작성할 수 있는지 소개하는 글입니다.

이 글은 Robert C. Martin의 글을 인용하는 걸로 시작하는데요, 정말 좋아하는 문장 중 하나 입니다.

“Test code is just as important as production code. It is not a second-class citizen. It requires thought, design, and care. It must be kept as clean as production code.”

이 글의 요약은 아래와 같습니다.

  1. 테스트의 구조를 Arrange-Act-Assert 패턴으로 잡으세요.

    • BDD에서 사용하는 Given-When-Then도 참고하면 좋습니다 :)

  2. 일반적으로 사용되는 객체들을 쉽게 설정하기 위해 테스트 객체 빌더를 사용하세요.

  3. 하나의 단위 테스트에선 하나의 개념만 테스트하세요.

  4. 테스트는 빠르고, 독립적이고, 반복할 수 있어야 하고, 자기 검증이 가능해야 하며(성공 또는 실패로 대표되어야 합니다.) 프로덕션 코드와 비슷한 시기에 작성해야 합니다.

  5. 테스트는 실패해야 할 때 실패해야 합니다.

  6. Happy Path 뿐만 아니라 Edge Case역시 테스트 해야 합니다.

  7. 잘못된 결과로 이어질 것 같은 케이스를 테스트해야 합니다.

더 자세한 내용은 원글을 참고해주세요 :)

원글:

https://betterprogramming.pub/clean-code-with-unit-tests-5f28020828a5

0
0
준프

준프

React Hook 지옥으로부터 벗어나기

리액트에서 Hook을 사용하다보면 알 수 없이 코드가 복잡해지고 점점 건드리기 무서워지기 마련입니다. 그건 아마 부수효과와 관리해야 하는 Hook이 많아져서 그런게 아닐까 막연하게 생각하곤 하는데요, 이번에 소개해드릴 글은 Hook을 어떻게 관리하면 좋을지 제안 하는 글입니다.

useEffect

"useEffect는 라이프사이클 Hook이 아니다"라는 말로 시작합니다. 예를 들어, 특정 상황(API server, strict development mode 등)에서 기대하지 않은 호출이 발생할 수 있습니다. 그리고 많은 useEffect의 사용은 가독성을 떨어뜨리고 유지하기 어렵게 만들며 테스트하기 어렵게 합니다.

useMemo

useMemo를 써야하는 곳이 아님에도 쓰는 건 '섣부른 최적화'라고 말합니다. useMemo를 남용하는건 메모리 이슈와 의존성을 신경쓰지 못해 발생하는 문제 등이 발생할 가능성을 높입니다.

useState

useState를 많이 사용하는건 한 함수에서 변수를 여럿 사용하는 것과 마찬가지로 가독성을 해치고 관리하기 어렵게 만듭니다.

이밖에도 useReducer, useContext, Hook 내부에 비즈니스 로직 등 좋은 내용이 많습니다. 이번에도 긴 글은 아니니 한 번 읽어보시는 걸 추천합니다 ! :)

0
2
심은광

심은광

GoogleSheets를 데이터 베이스로 활용하기 (feat. nextjs)

최근에 랜딩 페이지 제작 의뢰를 받게 되었는데,

데이터를 어디에 저장하는 게 좋을까 고민하다가 GoogleSheets API를 알게 되었고 바로 적용해 봤습니다.

현재 바로 활용할 수 있는 데이터베이스 서버가 없거나,

일회성 데이터인데 테이블까지 만들어서 사용하기가 번거롭게 느껴지시는 분이 계시다면 활용해 보시는 것을 적극 추천드립니다.

적용 결과부터 보여 드리고, 간단한 사용법을 설명해 보겠습니다.

[적용결과]

register.gif

1. Google Sheets API 등록하기

API 등록과 관련해서 잘 정리된 블로그 글이 있어서 아래 포스팅 참고해주시면 좋을 것 같아요 !!

2. NEXTJS API 활용하기

아래 코드는 nextjs api를 활용해서,

googleSheetAPI 권한을 인증하고, Sheet에 등록정보를 입력해 주는 간단한 api 코드입니다.

이해가 조금 어려울 수 있는 부분에는 주석을 남겨놨습니다.

import { NextApiRequest, NextApiResponse } from "next";
import { GoogleSpreadsheet } from "google-spreadsheet";
import { JWT } from "google-auth-library";

async function loadGoogleDoc() {
  try {
//  formattedKey: vercel에서 환경변수에 저장한 값은 \n이 \\n으로 인코딩되어 있어서 다시 디코딩해줘야함
    const formattedKey = process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, "\n");
//  Google Sheets API를 등록하는 과정에서 발급받은 키, 이메일을 입력해주세요.
    const serviceAccountAuth = new JWT({
      key: formattedKey,
      email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
      scopes: ["https://www.googleapis.com/auth/spreadsheets"],
    });
// Google Doc의 SheetId를 입력해주세요.
    const doc = new GoogleSpreadsheet(
      process.env.GOOGLE_SHEET_ID || "",
      serviceAccountAuth
    );
    await doc.loadInfo();
    return doc;
  } catch (error) {
    console.log(error);
  }
}

export default async function googleSheet(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === "POST") {
    try {
      const doc = await loadGoogleDoc();
      if (!doc)
        return res
          .status(200)
          .json({ ok: false, error: "사전등록에 실패했습니다." });
// "유저등록정보" 라는 이름의 sheet가 존재하는지 확인하고, 없다면 만들어줍니다.
      let sheet = doc.sheetsByTitle["유저등록정보"];
      if (!sheet) {
        sheet = await doc.addSheet({
          headerValues: ["email", "createdAt"],
          title: "유저등록정보",
        });
      }
// sheet에서 모든row 정보를 가져옵니다.
      const rows = await sheet.getRows();
// 이미 등록된 이메일인지 검증합니다.
      const isRegistered = rows.some(
        (row) => row.get("email") === req.body.email
      );
      if (isRegistered) {
        return res
          .status(200)
          .json({ ok: false, error: "이미 등록된 이메일입니다." });
      }
      const now = new Date();
      const utc = now.getTime() + now.getTimezoneOffset() * 60 * 1000;
      const koreaTimeDiff = 9 * 60 * 60 * 1000;
// sheet에 새로운 정보를 등록해줍니다.
      await sheet.addRow({
        email: req.body.email,
        createdAt: new Date(utc + koreaTimeDiff).toLocaleString(),
      });
      return res.status(200).json({ ok: true });
    } catch (error) {
      console.log(error);
      return res.status(500).json({ error: "Internal server error" });
    }
  }
}

위에서 작성한 api를 프론트엔드에서 활용하는 예시코드입니다.

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    try {
      e.preventDefault();
      if (isLoading) return;
      if (email === "") return;
      setIsLoading(true);
      const { data } = await axios.post("/api/googleSheet", {
        email: email,
      });
      if (data.ok) return message.success("사전등록이 완료되었습니다.");
      message.error(data.error);
    } catch (e) {
      console.log(e);
      message.error("사전등록에 실패했습니다.");
    } finally {
      setIsLoading(false);
    }
  };

마치며..

데이터 저장을 위한 서버를 따로 띄우는게 굉장히 번거롭게 느껴져서 저는 google sheets api를 활용해봤습니다 !!

혹시 이것보다 더 간편하고 좋은 방법을 알고계시는 분이 있으시면 저도 알려주세요 ㅎㅎ

읽어주셔서 감사합니다 !!

0
9
준프

준프

물어보는 문화 vs 추측하는 문화

이번에 소개하는 글은 물어보는 문화와 추측하는 문화에 대한 글입니다. 꽤 오래전에 추천 글로 올라왔었고 읽어야 겠다 생각 했지만 개발과 직접적으로 관련되어 있지 않고 다소 길다보니 미루고 있었는데요, 읽어보니 정말 좋은 내용인 거 같아 공유드립니다 :)

(요약문은 제가 의역한 부분이 많습니다. 그대로 옮기려니 조금 어색하더라구요... 하하)

먼저 이 글에서 말하는 물어보는 문화의 특징은

  1. yes라고 할 만한 요청이 아니더라도 무엇을 원하는지 물어봅니다.

  2. 다른 사람을 신경쓰기 보다 내가 필요한 걸 신경씁니다. 왜냐하면 yes라고 하기 싫다면 그럴 것이기 때문입니다.

  3. no라고 할 가능성이 높아보이더라도 요청하는 건 괜찮습니다.

  4. 사람들은 정말 괜찮을 때 yes라고 합니다. 그렇지 않다면 no라고 할 것입니다.

입니다.

다음으로 추측하는 문화의 특징입니다.

  1. 다른 사람이 yes라고 할만한 상황이라고 생각될 때에만 요청합니다.

  2. 요청하는 게 좋을지 판단하기 위해 간접적인 상황 단서들을 수집합니다.

  3. 다른 사람이 no라고 할만한 상황을 만드는 건 무례합니다.

  4. 사실 상황이 적절하고 그렇게 느껴진다면 요청을 하지 않아도 됩니다.

이 글에서 재미있는 예시가 있는데요, 질문하는 문화 안에서 추측하는 사람이 어떤 모습일지 보여줍니다.

"내가 일을 잘 한다는 사실은 분명하기 때문에 누군가 나를 툭툭 치면서 '매니저를 해주면 안 될까'라고 하길 바래"

라고 하는 사람이라는 것입니다.

반면 물어보고 요청하는 문화에선 요청을 받아줄거라 기대하지 않더라도 요청할 수 있어야 하고 요청에 대해 화내지 않고 거절할거라는 믿음이 필요합니다.

이 글을 보면서 많은 생각이 들었는데요, 특히 추측하는 문화에 익숙해 불편했던 점들이 떠올랐습니다. 그 중 가장 큰건 '내가 요청하지 않아도 내가 필요로 하는 인정을 받을 수 있는 마음'이 아닐까요?

그 밖에도 딱히 말하지 않아도 그 사람이 알아서 척척 일해주길 바라는 마음, 내가 어려움에 처해있을 때 요청하지 않아도 '어려운 일 있어?'라고 먼저 물어봐주길 바라는 마음이 있을 거 같습니다.

하지만 이 글에서 말하듯 요청한다는 건 거절 가능성을 전제로 하는 것이기 때문에 어렵습니다. 그럴 때 어떻게 하면 좋을지 아래와 같은 방법을 소개하고 있습니다.

  • 어떤 문제에서 헤어나오지 못하고 있다면 도움을 요청하기

  • 사람들이 no라고 하는 것에 좀 더 둔감해지기. 당신은 사람들이 no 라고 하지 않더라도, 사람들이 yes라고 할만한 요청만 하고 있을 것입니다. 그러니 좀 더 둔감해지고 좀 더 과감하게 요청하세요.

  • "내가 원하는 대로 할 수 있다면?"이라고 스스로에게 물어보세요. 이렇게 하면 다른 사람이 어떤 걸 원할지 생각해보지 않고 내가 원하는 걸 명확하게 요청할 수 있습니다.

추측 문화에 있으면 '뭐 먹고 싶어?'라고 물어볼 때 '아무거나'라고 대답한다고 합니다. 그리고 내가 먹고 싶은 걸 다른 사람이 선택해주길 기대하는 것이죠. 일을 하면서 이런 비슷한 일이 많은 거 같습니다. 물어보는 문화가 무조건 좋은 건 아니지만 추측하는 문화 역시 그렇지 않을까요?

조금 더 명확하게 요청하고 거절에 둔감해지는 걸 연습해보는 건 어떨까요?

그때가 6월경이었다. 나는 12월까지 권한대행 기간을 연장해주면 당해연도 지사의 실적 목표와 몇 가지 과제를 해결하는 것으로 내가 지사장 자격이 있는지 평가받고 싶다고 했다. 결과를 보고 적합한 사람이 아니라고 생각하면 그때 새로운 지사장을 다시 물색해서 영입하고 나는 원래 자리로 돌아가 역할을 다하겠다고 했다.

<나를 믿고 일한다는 것>, 우미영

0
5
문지웅

문지웅

섬네일을 만들어보자!

💡만들게 된 배경


블로그 작성 과정에서 필요한 섬네일 이미지 선택에 대한 고민이 생겨, 이 문제를 해결하기 위해 직접 섬네일 생성기를 개발하였습니다. 이 프로덕트는 '미니 픽(Mini Pick)'이라는 이름으로 명명되었는데, 이 이름은 ChatGPT로부터 제안 받은 10가지 이름 중에서 가장 적합하다고 판단하여 선택하였습니다.

⚒️ 기술 스택


Next.js , TypeScript, TailwindCSS, html-to-image , file-saver,Vercel

주요 기능

  1. 배경 설정 - 단색, 그라데이션 배경, 이미지 url 배경을 설정할 수 있습니다.

  2. 텍스트 설정 - 텍스트에 그림자를 주거나, 흰색 또는 검정색으로 설정할 수 있습니다.

  3. 반응형 디자인 - 반응형으로 설계되어, 저장하는 이미지의 넓이를 창의 크기에 따라 조절할 수 있습니다.

  4. 빠른 저장 - 저장하기 버튼 클릭했을 때, 바로 저장됩니다.

🖥️ 구현 과정


1. 프로젝트 생성

아래 명령어로 프로젝트를 생성합니다.

npx create-next-app@latest

세부 설정은 아래와 같습니다.

What is your project named? image-generator
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use src/ directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias? No / Yes

2. 프로젝트 구현

전체 프로젝트 구현 과정은 아래와 같습니다.

  1. 전체 레이아웃 구성

    1. 제목이 표시될 부분과 이미지 생성기가 표시될 부분을 구현했습니다.

  2. 제목 컴포넌트 구현

    1. 제목을 나타낼 Title 컴포넌트를 구현했습니다.

  3. 이미지 생성기 컴포넌트 구현

    1. 섬네일에 들어갈 문구를 입력 받는 부분을 구현했습니다.

    2. 배경 스타일을 설정하는 부분을 구현했습니다.

      1. 단색, 그라데이션, 이미지 배경이 가능합니다.

      2. 이미지 배경의 경우, 모달을 통해 입력받습니다.

    3. 텍스트 스타일을 설정하는 부분을 구현했습니다.

      1. 텍스트에 그림자를 추가할 수 있습니다.

      2. 텍스트 색상을 흰색 또는 검정색으로 설정할 수 있습니다.

    4. 이미지로 변환하기 전, 미리 보기할 부분을 구현했습니다.

      1. 넓이가 고정되어 있지 않아, 다양한 크기로 저장 가능합니다.

    5. 이미지를 저장할 저장하기 버튼을 구현했습니다.

      1. file-saver를 이용해서, 버튼을 눌렀을 때, 바로 저장되도록 구현했습니다.

3. 리팩토링

  1. 이미지 생성기 컴포넌트 분할

    • 초기에는 하나의 컴포넌트 안에서, 값 설정과 미리 보기를 함께 진행했습니다. 이를 두 개의 컴포넌트가 나누어 처리하도록 분리했습니다.

  2. html2canvas 에서 html-to-image로 전환

    • 초기에는 html2canvas를 이용해서, 미리 보기 div를 이미지로 저장하고자 했습니다. 해당 라이브러리 사용 시, url로 배경 이미지를 지정하면 이미지를 저장했을 때, 배경이 저장되지 않는 이슈가 생겼습니다. 이를 해결하기 위해, html2canvas 대신에, html-to-image로 전환했습니다.

구현 결과


아래 링크를 클릭하면 직접 확인하실 수 있습니다.

962shots_so.png

※ 전체 소스 코드는 여기서 확인할 수 있습니다.

감사합니다.

미니픽

미니멀한 블로그 썸네일 생성기

0
1
준프

준프

Optimistic UI에 대해

Optimistic UI에 대해 아시나요? Optimistic UI란 서버에서 응답이 오지 않더라도 "괜찮을 거야"라고 낙관하고 미리 결과를 화면에 노출하는 방법입니다. 그렇다면 Optimistic UI를 구현하기 위해 준비해야할 건 무엇일까요?

사실 UI를 구현하는데 고민할게 있을까? 싶을 수 있습니다. 평소엔 서버에 요청을 보내고 로딩 스피너를 노출했다면, 이젠 바로 완료 메세지를 보여주면 되지 않을까요?

사실 그렇지 않습니다. 서버에서 오류가 발생해 UI를 되돌려야 한다고 생각해보면 많은 이슈들을 고려해야 하기 때문입니다.

이번에 소개해드릴 아티클은 Optimistic UI를 구현하기위해 알아야할 그리고 준비해야할 것들을 명료하게 소개합니다.

가장 먼저 서버에서 실패하는 경우가 아주 희귀해야 한다는 전제 조건이 필요합니다. 그렇지 않다면 Optimistic UI를 구현하는 것 자체가 큰 비용일 수 있습니다.

그리고 실패란 코드 분석을 통해서 실패 발생 케이스가 0%이지 않고서는 실패가 발생할 가능성은 존재하기 때문에 대응하는 케이스를 준비해둬야 합니다. 그건 바로 클라이언트와 서버의 데이터 일관성과 동기화 입니다. 왜냐하면 실패했을 때 rollback을 할 책임을 클라이언트 역시 갖기 때문입니다.

더 자세한 내용은 아티클을 참고해주세요 ! 그리 길지 않습니다 :)

0
5
준프

준프

리액트 Custom Hook과 Context API

지겨울 만도 하지만 다시 나타난 Custom Hook과 Context API에 관한 글 입니다. 하지만 그럼에도 좋은 글이라고 생각해서 공유 합니다. 왜냐하면 사랑에 대한 글과 문장은 많지만 울림을 주는 경우는 많지 않고 명문은 더욱 적은 것과 비슷하다고 할까요?

종종 설명을 하다보면 장황하게 설명할 때가 많은데요, 이 글은 Custom Hook과 Context API가 리액트 앱에서 어떤 역할을 하는지, 언제 사용하면 좋을지 아주 간결하고 명확하게 설명합니다.

Custom hooks are functions that allow you to extract and share stateful logic across multiple components. They allow you to encapsulate state and behavior in a reusable, modular, and testable way. The Context API, on the other hand, provides a way to pass data down the component tree without having to pass props down manually at every level.

그리고 장점과 단점도 명확하게 나열되어 있습니다. 그 중에 특히 단점이 눈에 들어왔습니다.

  • Complexity: While custom hooks and the Context API are very powerful, they can also add a layer of complexity to your code, making it more difficult to understand and maintain.

글이 길지 않기 때문에 읽기도 좋네요 :)

0
1
준프

준프

가면 증후군과 압박감에 대해

가면 증후군(Imposter Syndrome)에 대해 아시나요?

이 증후군은 내 실력은 보잘것 없는데 인정 받을 때 내 진짜 실력이 드러나지 않을까 불안해하는 상태를 말합니다. 저도 이런 상태를 항상 경험하고 있는 편입니다. '난 잘 해내는 게 별로 없는데 나에 대한 기대치가 너무 높은게 아닐까?'하는 불안감은 때론 스스로를 너무 지치게 하는 것 같습니다.

이런 가면 증후군은 생각보다 많은 사람이 그리고 많은 개발자가 경험하고 있습니다. 이것과 관련해 좋은 글이 있어서 소개해 드리려고 합니다 !

한글로 작성되었고 좋은 내용이 많은 만큼 꼭 읽어보시는 걸 추천드립니다 !

가면 증후군은 압박감에 대한 방어기제가 아닐까 합니다. 압박(기대)을 많이 받다보니 불안감과 두려움이 커지는게 아닐까요. 그래서 압박감 해소를 위한 글도 하나 소개해드리려고 합니다.

이 글에선 압박감 해소를 위해 몇 가지 방법을 소개하고 있습니다.

  1. 현실을 객관적으로 평가하기 - 압박이 강하다면 '만약?'이라는 패턴에 갇히게 되는데, 거기에서 빠져나와 현실과 사실에 집중합니다.

  2. 불안감을 기대감으로 바꾸기 - 연구 결과에 따르면 불안감을 위협이 아니라 기회로 보는 사람들의 생산성이 더욱 높았다고 합니다. 불안한 상황에서 '침착하게 대응하자'라고 하는 대신 '난 지금 매우 기대돼'라고 해보는 건 어떨까요.

  3. 과거의 경험을 활용하기 - 이전에 비슷한 경험을 통해 성공해본 사례가 있나요? 지금 불안한 상황이 미래의 잠재적 성공으로 가기 위한 길은 아닌지 과거의 경험을 통해 살펴보는 건 어떨까요.

  4. 아주 작은 개선 - 불안감은 사람을 옴짝달싹 못하게 만들곤 합니다. 대신 아주 작더라도 지금 당장 개선할 수 있는 것에 집중합니다. 먼 길도 눈 앞의 한 걸음부터 시작이라는 사실을 잊지 않습니다.

  5. 항상 준비되어 있는 상태 - 스스로를 예상하지 못한 어려움을 예상할 수 있도록 훈련합니다. 침착하게 대응하기 위해 최악의 상황이 벌어질 수 있다는 사실을 받아들이고 준비합니다.

여기에 있는 내용들은 진부하게 느껴질 수도 있지만, 기억해 둔다면 압박감 때문에 움직일 수가 없을 때 한 번쯤 시도해보면 좋은 내용들이라고 생각합니다. 모두의 건강한 일상을 응원합니다 !

0
0
준프

준프

이벤트 버스로 결합도를 낮추기

이번에 소개해드리는 글은 '이벤트 버스'와 관련된 글입니다. 특히 이벤트 버스를 활용해 불필요한 결합을 제거하는 방법을 소개하고 있습니다. 특히 리액트 예시가 눈길을 끌었습니다.

이벤트 버스라는 단어가 잘 와닿지 않을 수도 있는데요, 가장 간단한 예시로 'input' 이벤트가 있습니다.

// HTML
<label for="nameInput">이름</label>
<input type="text" id="nameInput" />

// javascript
const $input = document.querySelector('#nameInput');

$input.addEventListener('input', (event) => {
  console.log('이름이 바뀌었어요 !', event.target.value);
});

input에 텍스트를 입력하면 브라우저에선 이벤트를 발생시킵니다. 이 과정은 텍스트 입력이라는 승객을 브라우저라는 버스에 태운 것으로 해석할 수 있습니다. 그리고 브라우저는 승객을 이벤트의 형태로 이벤트 리스터 정착지에 내려줍니다.

이 과정은 브라우저에서 기본으로 제공하는 버스라면 우리가 버스를 만들 수도 있는데요, 이 글에선 우리가 만드는 버스를 통해 프론트엔드에서 결합도를 낮추는 방법을 소개하고 있습니다. 이 글에선 아래와 같은 버스를 소개하고 있습니다.

let model = {
  value: '',
  set(val) {
    if (val === this.value) return
    this.value = val
    window.dispatchEvent(new CustomEvent('modelUpdate', { detail: this }))
  },
}

그리고 이벤트 버스를 사용했을 때 장점과 단점도 소개하고 있습니다.

An event bus allows us to loosen the coupling between the two parts of the code. By loosening the coupling between the code, we gain the ability to replace one part without affecting the other, which means the software becomes easier to modify. At least in theory.
...
A disadvantage of this indirect way of communicating is that it’s more difficult to identify who is being called or who is doing the calling or even if there are any callers or callees to begin with (because that’s what gives it the advantages).

사실 우리가 이벤트 리스너를 사용했을 때 장점과 단점을 기억해보면 이해하기 쉬울 거 같습니다.

제 경우 정확히 기억이 나진 않지만 커스텀 이벤트를 통해 문제를 해결했던 경험이 2-3회 정도 있었는데요, 알아두면 언젠가 도움이 되지 않을까 합니다.

0
2
준프

준프

일반적인 프론트엔드 프로젝트에서 오버엔지니어링 하지 않기

오늘 소개해드리는 이 글은 우리가 일상적으로 만드는 프론트엔드 프로젝트가 어떤 걸 갖추면 좋은지 이야기 합니다. 반대로 어떤 요소들이 오버엔지니어링을 발생시키는지 말해줍니다.

이 글이 좋았던 이유는 여기에서 말하는 대부분의 것들은 생각보다 일정 규모 이상의 조직에서 경험하게 된다는 것입니다. 즉, 제가 경험하고 들어본 대부분의 작은 조직에선 이 글에서 말하는 '갖추면 좋은' 구성도 고려 대상이 아닙니다. 그들 역시 회사를 먹여살리는 일상적인 규모의 프론트엔드 프로젝트를 운영함에도 그렇습니다.

그렇기 때문에 이 글이 눈에 들어왔습니다. 프로젝트를 구성할 때 참고하면 도움이 되지 않을까 합니다.

이 글에서 추천한 몇몇 섹션에 대해 제 생각을 공유하는 걸로 마무리 해보겠습니다 :)

상태 관리

상태 관리가 반드시 필요한가? 라고 하면 고개를 갸우뚱하게 됩니다. 그렇다고 해서 원천적으로 '아니다'라고 하는 건 아닙니다. 이 글에서 말하는 것처럼 애플리케이션이 커지고 복잡도가 올라갈 때 상태관리를 고려하는 건 충분하다고 생각합니다. 물론 불필요한 상황에서 미리 도입하고 보는 건 오버엔지니어링이 될 수 있다고 생각합니다.

기능 플래그

기능 플래그는 새로운 기능을 배포할 때 아주 유용한 도구 입니다. 일정 규모 이상의 앱을 개발한다면 적극적으로 고려해볼만 하다고 생각합니다.

테스트

이 글에서 말하는 것처럼 테스트는 필수입니다. 하지만 어떤 테스트냐에 따라 다를 수 있습니다. 우리 앱의 안정성을 높이는 데 맞는 테스트를 고민해보고 적용하는 과정이 있어야 한다고 생각합니다.

모니터링 (Observability)

모니터링은 고객으로부터 문제를 보고받기 전, 그리고 고객이 느끼지 못하는 잠재적인 문제를 해결할 수 있도록 도와줍니다. 그리고 문제가 발생했을 때 원인 파악을 비교적 정확하게 할 수 있도록 도와줍니다.

접근성

접근성은 앱에서 갖춰야할 가장 중요한 요소 중 하나 입니다. 현재 만들고 있는 앱이 사용자의 접근성을 해치진 않는지 한 번 검토해보는 것도 중요하다고 생각합니다.

DDD, 헥사고날 아키텍처, 마이크로 프론트엔드

모두 오버엔지니어링으로 카테고라이징이 되어 있습니다. 저도 그렇게 생각합니다. 대규모 프로젝트가 아니고서는, 심지어 대규모 프로젝트라고 하더라도 도입은 충분한 고민 후에 그리고 필요에따라, 무엇보다 구성원의 동의와 함께 도입되어야 한다고 생각합니다. 왜냐하면 진입장벽이 존재하고 진입장벽이 존재한다는 사실 자체만으로 앱의 안정성을 해칠 가능성이 있기 때문입니다.

디자인시스템

디자인 시스템은 앱 개발의 생산성을 높여주는 중요한 요소 중 하나 입니다. 하지만 이 글에서 언급한 것처럼 처음부터 발명하고 구현하고 유지보수 하는 건 일정 규모의 조직이 아니고선 생각보다 많은 비용을 발생시키는 오버엔지니어링 입니다. 대신 외부 라이브러리를 가져와 그대로 사용하거나 우리 앱에 맞게 커스터마이징 하는 걸 고려해보는 게 좋다고 생각합니다.

0
5
다운

다운

쓸만한 리액트 훅 라이브러리 react-use, useHooks

재사용성 괜찮은 react hook은 작업할 때마다 간혹 찾곤 하는데요, 오늘 @uitodev/usehooks를 처음 알게 된 김에 관련된 훅 라이브러리까지 공유드려봅니다 :) 이외에도 자주 사용하시는 훅 라이브러리 있으시면 추천 부탁드립니다!

1. react-use

필수 React Hook 모음입니다. libreact의 포트라고 하네요. 저는 useSessionStorage, useLocalStorage, useInterval 등을 사용했었어요.

2. useHooks

서버 사이드 렌더링에서도 쓸 수 있는 안전한 훅 모음 - ui.dev 팀에서 유지보수하고 있고 react-use보다 조금 더 최신 기술들이 많이 반영돼있어요.

CleanShot 2023-07-21 at 15.41.49.png

참고자료

0
1
준프

준프

값 객체에 대해 아시나요?

프론트엔드 개발자로서 일을 하다보면 종종 나누는 이야기가 있습니다. 그 중 하나는 바로

"프론트엔드 개발을 하면서 클래스를 써본 경험이 있나요?"

입니다. 전 종종 쓰기도 하고 곧 블로그로 글을 쓸 예정인데요, 이번에 소개해드릴 글은 클래스를 사용할 때 유용한 개념 중 하나인 '값 객체Value Object' 입니다. 값 객체는 이미 존재하는 타입(예를 들어 string, Date 등)을 통해 만드려는 시스템을 표현하는 데 한계가 있을 때 사용합니다.

예를 들어, 아래와 같이 개발하실 때가 많을 텐데요.

const startDate = new Date(2023, 1, 1);
const endDate = new Date(2023, 1, 2);

if (startDate > endDate) {
  // 값이 잘못됐다는 오류
}

// 이 코드가 반복돼서 아래와 같이 함수 제작

function isStartGreaterThanEnd(startDate, endDate) {
  return startDate > endDate;
}

if (isStartGreaterThanEnd(startDate, endDate)) {
  ...
}

이렇게 하는 대신 값 객체를 사용하면 아주 그럴싸(응집도가 올라가는 등)해집니다.

class EventDate {
  #date;
  constructor(date) {
    this.#date = date;
  }

  ...

  greaterThan(date) {
    return this.#date > date;
  }
}

const startDate = new EventDate(new Date(2023, 1, 1));
const endDate = new EventDate(new Date(2023, 1, 2));

if (startDate.greaterThan(endDate)) {
  ...
}

}

하지만 이런 값 객체를 사용하려면 몇 가지 지켜야할 내용이 있는데요, 찾아보면 생각보다 어려운 개념들이 많고 DDD와 관련된 내용도 많아서 접근하기가 꺼려지는게 사실입니다.

그래서 길가다 주운 좋은 아티클을 소개드리려고 합니다. (영어지만 요즘은 AI가 번역해준다는 사실... ㅎㅎ)

그리고 조만간 관련해서 블로그를 통해 소개해드리겠습니다 !

0
0
준프

준프

프론트엔드에서 Headless란?

(출처: https://itnext.io/decoupling-ui-and-logic-in-react-a-clean-code-approach-with-headless-components-82e46b5820c)

이런저런 아티클을 읽다보면 자주 마주치는 개념들이 있는데 그 중 하나가 'headless' 입니다. 전 아직도 headless가 무엇인지 헷갈릴 때가 있는데요, 오늘은 headless에 대한 아티클을 공유해보려고 합니다.

Bard의 답변

The word "headless" can have several meanings, depending on the context.

  • Without a head. This is the most literal meaning of the word. It can refer to a person or animal that has had its head cut off, or to a device or object that does not have a head. For example, a headless printer is one that does not have a display screen.

  • Without a leader or chief. This meaning is more figurative. It can refer to a group or organization that does not have a clear leader or authority figure. For example, a headless company is one that is struggling to find a new CEO after the previous one resigned.

  • Foolish or stupid. This meaning is informal and is often used in a humorous way. It can refer to someone who is making a silly mistake or who is not thinking clearly. For example, you might say that someone made a headless argument if they were not making any sense.

In the context of technology, the term "headless" is often used to describe a device or application that does not have a graphical user interface (GUI). This means that it does not have a display screen or any other visual elements. Instead, it is controlled through a command-line interface (CLI) or a web API.

For example, a headless browser is a web browser that does not have a graphical user interface. It is used by developers to test web pages and applications without having to display them on a screen.

I hope this helps! Let me know if you have any other questions.

아래에 소개해드리는 몇몇 아티클을 요약해보면 headless는

"마크업(HTML), 스타일(CSS), 구현 등을 제외한 핵심 로직을 제공하는 라이브러리 또는 유틸리티 입니다."

가장 유명한 Counter 컴포넌트를 예로 들면, 라이브러리에선 숫자를 세는 로직을 분리해서 제공하고 Counter 컴포넌트를 만드는 데 사용하는 HTML, CSS 그리고 갯수를 어떻게 활용할지(구현) 등은 라이브러리 사용자에게 위임하는 것입니다.

반면, Headless Design System은 어떨까요? 프론트엔드 개발을 할 때 '모달'을 가져다 쓴다면 라이브러리나 디자인시스템의 차이를 구분하시나요? 또는 라이브러리를 'headless' 인지 아닌지 구분하시나요? 제가 혼동하던 부분은 이 부분이었던 것 같기도 합니다.

아래 아티클은 Headless Design System에 대해 소개합니다.

On the other hand, the term “headless” refers to an architecture where the front-end and back-end systems operate independently from one another. In this case, a headless design system allows you to use a single design system across multiple applications and platforms while decoupling it from any specific technology, integration, or UI.

"'headless'는 서로 독립적으로 운영하는 프론트엔드 그리고 백엔드 아키텍처를 의미합니다. 이 경우, 'headless' 디자인 시스템은 여러 애플리케이션과 플랫폼 사이에서 특정 기술, 통합 또는 UI로부터 분리해서 디자인시스템을 사용할 수 있도록 합니다."

즉,

"디자인시스템을 사용하려는 환경을 고려하지 않아도 쓸 수 있도록 설계된 디자인시스템을 'headless' 디자인시스템이라고 한다."

입니다.

결국 'headless'란 사용하는 곳의 환경에 의해 쉽게 바뀔 수 있는 부분을 제외한 핵심적인 부분을 의미하는 것 같습니다.

만약 더 구체적인 예제와 이해를 하고 싶다면 위에 소개해드린 아티클들을 참고해주세요 :)

1
2