GN⁺: OpenAI는 코드 복원(Unminifying)에 놀라운 성능을 보임
(glama.ai)- 인터넷을 탐색하던 중 흥미로운 ASCII 아트 컴포넌트를 발견함. 멋져 보였지만 구현 방법을 이해하기 어려웠음
- 소스 코드를 확인했으나 코드가 축약(Minified)되어 있었음
축약된 코드 예시
- 코드 일부는 다음과 같음:
const { floor: ra, abs: KE, min: QE } = Math, O5 = ["reactive.network REACTIVE.NETWORK", "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~i!lI;:,^`'. .:â–‘â–’â–“â–ˆ"], G7 = Date.now() % 3 ? O5[1] : O5[0], V5 = G7.length, JE = { fps: 60 };
ChatGPT에게 코드 설명 요청
- ChatGPT에게 "코드를 설명해 달라"고 요청함
- ChatGPT의 응답:
제공된 코드는 상당히 복잡하지만, 각 부분과 기능을 이해하기 쉽게 설명하겠습니다.
개요
- JavaScript를 사용하여 여러 함수와 컴포넌트를 정의함
- React 애플리케이션에서 동적 텍스트 콘텐츠를 생성함
- ASCII 아트 또는 미리 정의된 문자 세트를 사용한 시각적 효과를 생성함
주요 컴포넌트와 함수
- 문자 세트 선택:
const { floor: ra, abs: KE, min: QE } = Math;
const O5 = ["reactive.network REACTIVE.NETWORK", "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~i!lI;:,^`'. .:â–‘â–’â–“â–ˆ"];
const G7 = Date.now() % 3 ? O5[1] : O5[0];
const V5 = G7.length;
-
Math
의 함수들을 가져오고 두 개의 문자 세트를 정의함 - 현재 시간에 따라 문자 세트를 선택함
- 선택된 문자 세트의 길이를 저장함
- 동적 문자 선택 함수:
function eT(e, t, n, r) {
const i = t.time * 8e-5;
const s = QE(t.cols, t.rows);
const o = t.metrics.aspect * 0.2;
const l = { x: ((4 * (e.x - t.cols / 6.25)) / s) * o, y: (5 * (e.y - t.rows / 4)) / s };
const u = ra(KE(YE(l) - i) * V5 + (ra(e.x / 1) % 2) * 2) % V5;
return G7[u];
}
-
eT
함수는 입력 위치와 현재 시간에 따라 문자를 생성함 - 위치를 조정하고 문자 세트에서 문자를 선택함
-
동적 콘텐츠를 표시하는 React 컴포넌트 (
tT
):
const tT = () => {
const e = j.useRef(null);
const [t, n] = j.useState({ height: null, width: null });
j.useEffect(() => {
function r() {
n({ height: window.innerHeight, width: window.innerWidth });
}
if (typeof window < "u") {
n({ height: window.innerHeight, width: window.innerWidth });
window.addEventListener("resize", r);
return () => window.removeEventListener("resize", r);
}
}, []);
j.useEffect(() => {
const r = e.current;
if (!r) return;
const i = 12;
const s = ra(t.width / i) * 1.6;
const o = ra(t.height / i);
const l = { aspect: s / o };
const u = setInterval(() => {
let c = "";
for (let d = 0; d < o; d++) {
for (let f = 0; f < s; f++) c += eT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() });
c += ` `;
}
r.textContent = c;
}, 1e3 / JE.fps);
return () => clearInterval(u);
}, [t]);
return a.jsx("div", { style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%" }, children: a.jsx("div", { ref: e, style: { width: "100%", height: "100%", whiteSpace: "pre", overflow: "hidden" } }) });
};
- 이 React 컴포넌트는 주기적으로
div
요소의 콘텐츠를 동적으로 생성된 문자로 업데이트함 - 창 크기에 따라 열과 행의 수를 계산하고
div
를 문자로 채움
TypeScript로 변환된 코드
- ChatGPT에게 TypeScript로 변환된 코드를 요청함
- ChatGPT의 응답:
import React, { useEffect, useRef, useState } from 'react'; const selectedCharacterSet = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~i!lI;:,^`'. .:â–‘â–’â–“â–ˆ"; const characterSetLength = selectedCharacterSet.length; const calculateCharacter = ( x: number, y: number, cols: number, rows: number, aspect: number, time: number, ): string => { const timeFactor = time * 0.000_08; const size = Math.min(cols, rows); const aspectRatio = aspect * 0.2; const position = { x: ((4 * (x - cols / 6.25)) / size) * aspectRatio, y: (5 * (y - rows / 4)) / size, }; const index = Math.floor( Math.abs( Math.cos(position.x * position.x - position.y * position.y) - timeFactor, ) * characterSetLength + (Math.floor(x) % 2) * 2, ) % characterSetLength; return selectedCharacterSet[index]; }; type Size = { height: null | number; width: null | number; }; const AsciiArtGenerator: React.FC = () => { const textRef = useRef(null); const [size, setSize] = useState({ height: null, width: null }); useEffect(() => { const handleResize = () => { setSize({ height: window.innerHeight, width: window.innerWidth }); }; handleResize(); window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, []); useEffect(() => { const element = textRef.current; if (!element || !size.width || !size.height) return undefined; const cols = Math.floor(size.width / 12) * 1.6; const rows = Math.floor(size.height / 12); const aspectRatio = cols / rows; const intervalId = setInterval(() => { let content = ''; for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { content += calculateCharacter(x, y, cols, rows, aspectRatio, Date.now()); } content += '\n'; } element.textContent = content; }, 1_000 / 60); return () => { clearInterval(intervalId); }; }, [size]); return ( ); }; export default () => { return ; };
GN⁺의 정리
- 이 글은 축약된 코드를 이해하고 복원하는 방법을 설명함
- ChatGPT를 사용하여 복잡한 코드를 설명하고 TypeScript로 변환하는 과정을 보여줌
- 코드 복원과 이해를 돕기 위해 AI 도구를 활용하는 방법을 제시함
- 비슷한 기능을 가진 프로젝트로는
Prettier
와UglifyJS
가 있음
Hacker News 의견
-
HumanifyJS의 저자가 LLM 기반 도구를 개발했음. 이 도구는 AST 수준에서 LLM을 사용하여 코드가 unminification 후에도 작동하도록 보장함
-
JS minification은 기계적이고 비교적 간단함. 수동으로 수행하기에는 번거롭지만, 변환 자체는 제한적이어서 일부 노트로 식별자를 추적하며 읽을 수 있음
-
일반적인 unminification이나 unobfuscation은 여전히 해결되지 않은 문제임. 과거에 의도적으로 난독화된 프로그램을 작성했는데, ChatGPT는 표면 수준에서도 이해하지 못했음
- 예: 160바이트 길이의 Brainfuck 인터프리터를 GPT-4가 설명하려 했으나 원본 코드와 전혀 유사하지 않았음
- 예시 코드 링크
-
일부 개발자는 난독화를 위해 minification을 사용하기 때문에 불만을 가질 수 있음. ColdFusion의 이전 버전에서 제공된 코드 "암호화" 도구가 약한 알고리즘이었음에도 불구하고, 이를 안전하다고 생각하고 사용한 사람들이 있었음
-
제목에 OpenAI 대신 ChatGPT나 gpt-4를 사용하는 것이 더 적절할 수 있음
-
이 작업은 인간보다 우수한 지적 성능의 예임. LLM은 unminification을 이해하고 있으며, 이 특정 주제에서는 인간보다 우수함
-
LLM은 텍스트 변환에 뛰어남. 이것이 그들의 핵심 강점임
-
약 1년 전, GPT를 사용하여 악성 코드의 난독화를 시도했으나 직접 코드를 분석하는 것이 더 나았음. 최근 다시 시도했을 때는 어느 정도 이해할 수 있었음
- 예: 페이지 로드를 감지하고, 계산을 수행하며, 악성 사이트로 리디렉션하는 로직을 파악함
- 보고서 링크
-
LLM을 사용하여 복잡한 코드를 더 읽기 쉽게 만드는 것이 매우 해방감을 줌. 때로는 세부 사항을 놓치지만, 이는 직접 확인할 수 있음
- 코드의 목적은 이해하지만, 특정 방식으로 작성된 이유를 이해하지 못할 때 LLM에 질문하여 새로운 통찰을 얻을 수 있음
- 사용 예시 링크
-
unminified 코드가 minified 코드와 일치하지 않으면 유용성이 크게 감소함. 많은 경우 코드 실행 후 차이를 확인할 수 없음
-
웹사이트의 계산 알고리즘을 알고 싶을 때 minified JS를 ChatGPT에 넣어 쉽게 파악할 수 있었음. LLM이 전체 SAAS 앱을 클론할 수 있는 미래를 상상할 수 있음
- 예: HubSpot 클론을 만들기 위해 LLM이 브라우저와 상호작용하고 UI를 분석하며 백엔드 아키텍처를 제안할 수 있음
- AI가 미래에 강력한 디플레이션 요인이 될 것임