본문 바로가기

사이드 프로젝트 아카이빙

[Identicon library] 랜덤 프로필 이미지 생성하기

반응형

개요

#1. 기본 사용자 프로필 이미지 생성

사용자 프로필을 구성할 때 이미지를 업로드 하기로 했다.

 

원하는 이미지를 프로필 사진으로 정하는 일반적인 방식인데
만약 사용자가 이미지를 업로드 하지 않는다면 기본 이미지가 필요해진다.

 

Github에서 사용하는 랜덤 프로필 이미지 생성기를 사용하면 좋겠다 생각해서 적용해봤다.

라이브러리는 Identicon을 사용했다.

 

GitHub · Change is constant. GitHub keeps you ahead.

Join the world's most widely adopted, AI-powered developer platform where millions of developers, businesses, and the largest open source community build software that advances humanity.

github.com


내용

#1. Identicon이란?

Identicon은 Github/StackOverflow 등에서 볼 수 있는 데이터(문자열) 기반 랜덤 이미지이다.

Identicon 이미지 예시

 

개발자라면 많이 접했을 이미지라고 생각한다.

(Github은 다 사용하니까 못 봤다고 하면..)

 

Identicon은 16진수 문자열(seed)을 기반으로 이미지를 생성한다.
따라서 아래 규칙을 만족시키는 Seed가 필요하다.

조건 이유
문자열 기반 사용자마다 다름
변환 규칙 고정 항상 동일한 Seed 생성
16진수(Hex) 변환 Identicon.js 요구사항

 

나는 사용자의 이름 혹은 닉네임을 16진수 문자열로 변환해 Seed로 사용했다.
(동명이인 등을 고려하면 이메일로 Seed를 생성할 걸 그랬다.)


#2. 사용법

라이브러리를 사용해야 하니 아래 명령어로 설치부터 진행한다.

npm install identicon.js

 

또는

yarn add identicon.js

 

라이브러리를 Import 해주고

import Identicon from 'identicon.js';

 

Identicon 생성자에 Parameter를 차례대로 넣어주면 자동으로 이미지가 생성된다.

  • hash(string): 변환 기준 Seed (필수)
  • foreground(Color): 도형(패턴) 부분의 색상 설정, 서비스 브랜드 컬러 기반으로 커스터마이징 등에 활용
  • background(Color): 아이콘 배경 영역 색상
  • size(number): 아이콘 이미지 크기 (px 단위)
  • format("svg" | "png"): 이미지 출력 형식 (필수) (svg 권장)
  • margin(number): 이미지 및 패턴 사이의 padding 적용;

우리는 생성되는 이미지를 Base64 형식으로 인코딩해서 data url scheme으로 만든다.

import Identicon from 'identicon.js';

// seed 값(문자열) 기반 hex 문자열 생성
const seed = 'username123';
const hex = Array.from(seed)
  .map((c) => c.charCodeAt(0).toString(16))
  .join('');

const identicon = new Identicon(hex, 100).toString();

// Data URL 형태로 img 태그에 바로 적용 가능
const imgSrc = `data:image/png;base64,${identicon}`;

console.log(imgSrc); // data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG...

 

생성된 이미지 경로를 img 태그의 src 속성에 할당하면 끝이다.

<img id="avatar" width="100" height="100" />
<script type="module">
  import Identicon from 'identicon.js';

  const seed = 'example';
  const hex = Array.from(seed).map(c => c.charCodeAt(0).toString(16)).join('');
  const data = new Identicon(hex).toString();

  document.getElementById('avatar').src = `data:image/png;base64,${data}`;
</script>

#3. 프로젝트 적용

기본적인 사용법을 익혔으니 실제로 사용해 보겠다.

동일한 사이즈와 이미지 확장자로 생성되기를 기대하기 때문에 기본 옵션을 정의한다.

const DEFAULT_IDENTICON_OPTIONS = {
  size: 300,
  format: 'svg',
};

 

기준이 되는 문자열을 16진수 문자열(Seed)로 변환하는 메서드를 정의한다.

// seed → 16진수 문자열 변환
const normalizeSeed = (seed) => {
  const base = seed || 'carhartt-user'; // seed fallback
  const hex = Array.from(base)
    .map((char) => char.charCodeAt(0).toString(16))
    .join('');
  // identicon.js는 최소 15자 이상의 hex 문자열 요구
  return hex.padEnd(32, '0').slice(0, 32);
};

 

[ ⚠️ 주의 ]

normalizeSeed 메서드에서 'Seed'가 없을 때 fallback 문자열(carhartt-user)이 적용되는데,
이 경우 모든 사용자가 동일한 Identicon을 갖게 된다.
따라서 실제 서비스에서는 memberId, email 등
각 사용자 고유 값을 기반으로 Seed를 설정하는 것이 중요하다.
(개인정보 사용 시에는 Hasing(SHA256) 등을 적용해 보안을 고려해야 한다.

 

normalizeSeed로 생성된 Seed를 Identicon의 Parameter로 넘겨 이미지를 생성하고
data url scheme으로 변환해 반환한다.

// Data URL로 변환
export const generateIdenticonDataUrl = (
  seed,
  options = DEFAULT_IDENTICON_OPTIONS
) => {
  const hexSeed = normalizeSeed(seed); // 16진수 문자열(Seed)
  const { size, format } = { ...DEFAULT_IDENTICON_OPTIONS, ...options }; // 기본 사이즈와 이미지 확장자
  const identicon = new Identicon(hexSeed, { size, format }).toString(); // 이미지 생성 결과
  const mimeType = format === 'svg' ? 'svg+xml' : 'png'; // mime 타입 정의 (혹시 몰라 'png'도 처리)
  return `data:image/${mimeType};base64,${identicon}`; // data url scheme형태로 반환
};

 

16진수 변환기(normalizeSeed)와 이미지 생성기(generateIdenticonDataUrl)을 조합해서
최종 랜덤 프로필 이미지 생성기(makeUserAvatar)를 정의한다.

// 기존 프로필 이미지 키 후보
const STRING_FALLBACKS = ['avatar', 'profileImage', 'profile_image', 'image'];

// 값 검증: 비어있지 않은 문자열만 유효
const isValidImageValue = (value) => {
  if (value === null || value === undefined) return false;
  if (typeof value !== 'string') return true;
  const trimmed = value.trim();
  if (trimmed === '' || trimmed.toLowerCase() === 'null') return false;
  return true;
};

// 사용자 객체에 avatar 자동 생성
export const makeUserAvatar = (user, options) => {
  if (!user) return user;

  // 상수로 정의한 속성 key가 사용자 객체(use)에 있다면 사용
  const avatarKey = STRING_FALLBACKS.find(
    (key) => key in user && isValidImageValue(user[key])
  );

  if (avatarKey) {
    return {
      ...user,
      avatar: user[avatarKey],
    };
  }

  // 없다면 닉네임/이름 기반 Identicon 적용
  const seed = user.memberName || user.memberNickname;
  const avatar = generateIdenticonDataUrl(seed, options);

  return {
    ...user,
    avatar,
  };
};

 

이제 img 태그의 src 속성에 결과를 잘 넣어주면 완성이다.

export default function UserAvatar({ user }) {
  const avatarUser = makeUserAvatar(user);

  return (
    <img
      src={avatarUser.avatar}
      alt={`${avatarUser.memberName} avatar`}
      width={48}
      height={48}
    />
  );
}

Identicon 이미지 적용 결과

 

아래는 전체 코드이다.

import Identicon from 'identicon.js';

const DEFAULT_IDENTICON_OPTIONS = {
  size: 300,
  format: 'svg',
};

// 기존 프로필 이미지 키 후보
const STRING_FALLBACKS = ['avatar', 'profileImage', 'profile_image', 'image'];

// 값 검증: 비어있지 않은 문자열만 유효
const isValidImageValue = (value) => {
  if (value === null || value === undefined) return false;
  if (typeof value !== 'string') return true;
  const trimmed = value.trim();
  if (trimmed === '' || trimmed.toLowerCase() === 'null') return false;
  return true;
};

// seed → 16진수 문자열 변환
const normalizeSeed = (seed) => {
  const base = seed || 'carhartt-user'; // seed fallback
  const hex = Array.from(base)
    .map((char) => char.charCodeAt(0).toString(16))
    .join('');
  // identicon.js는 최소 15자 이상의 hex 문자열 요구
  return hex.padEnd(32, '0').slice(0, 32);
};

// Data URL로 변환
export const generateIdenticonDataUrl = (
  seed,
  options = DEFAULT_IDENTICON_OPTIONS
) => {
  const hexSeed = normalizeSeed(seed);
  const { size, format } = { ...DEFAULT_IDENTICON_OPTIONS, ...options };
  const identicon = new Identicon(hexSeed, { size, format }).toString();
  const mimeType = format === 'svg' ? 'svg+xml' : 'png';
  return `data:image/${mimeType};base64,${identicon}`;
};

// 사용자 객체에 avatar 자동 생성
export const makeUserAvatar = (user, options) => {
  if (!user) return user;

  const avatarKey = STRING_FALLBACKS.find(
    (key) => key in user && isValidImageValue(user[key])
  );

  if (avatarKey) {
    return {
      ...user,
      avatar: user[avatarKey],
    };
  }

  // 없다면 닉네임/이름 기반 Identicon 적용
  const seed = user.memberName || user.memberNickname;
  const avatar = generateIdenticonDataUrl(seed, options);

  return {
    ...user,
    avatar,
  };
};

정리

Identicon을 사용할 때 유용한 팁을 정리해봤다.

 

내용 추천 방법
Seed 선택 기준 이메일, memberId 등이 안정적 (삭제/변경 적음)
SVG vs PNG SVG 권장 (가볍고 배율 대응 우수)
캐싱 Data URL 자체가 브라우저 캐시 활용
민감 정보 해싱? Seed에 직접 이메일 넣기 부담이라면 해시(SHA256) 적용 가능
서버 vs 클라이언트 생성 로그인 직후 클라이언트 처리 권장

 

이로써 사용자의 정보가 없는 경우에도
Identicon 기반 아바타가 자동 생성될 수 있도록 처리했다.

 

이 방식의 핵심은

✔ 동일한 seed → 동일한 이미지
✔ seed 규칙 통일 → UI 일관성 유지

이다.

반응형