카테고리 없음

ICT 인턴십 SWINK 포트폴리오 정리

LetsCoding 2026. 10. 16. 02:20

즉:

Stretch
→ 한 텍스처를 길게 늘임
→ Texture 왜곡 문제

Sprite Chain
→ 여러 Sprite를 이어 붙임
→ 연결부 / 겹침 / 각짐 문제

Ribbon Mesh + UV Tiling
→ 하나의 연속 Mesh
→ UV를 거리 기반으로 반복
→ 연결부 감소 + Texture 밀도 유지

따라서 네 이미지가 정확히 기존 구현을 나타낸다면, 지금 문장의:

“단순 Stretch 방식에서 발생하던 텍스처 왜곡을 줄였습니다.”

는 Sprite Chain의 문제를 설명하는 문장으로는 정확하지 않아.

차라리 이렇게 쓰는 게 좋아.

Stroke의 누적 이동 거리를 기준으로 UV 좌표를 계산해 Ribbon 길이와 관계없이 모래 텍스처가 일정한 밀도로 반복되도록 구현했습니다. 기존 Sprite Chain 방식에서 발생하던 이미지 연결부와 구간별 텍스처 밀도 차이를 줄이고, 하나의 연속된 Ribbon 형태로 표현했습니다.

만약 실제 구현에서 Sprite 하나를 각 구간 길이에 맞게 늘려가며 Chain을 만들었다면 Stretch 문제도 동시에 있었다고 말할 수 있어. 하지만 Sprite Chain = Stretch라고 동일시하면 안 돼.

그리고 하나 더. 현재 문서에는 첫 문장이:

기존 RenderTexture 기반 구현에서는...

이라고 되어 있는데 그림은:

기존 Sprite Chain

이라고 되어 있잖아.

둘이 실제로 서로 다른 이전 구현 단계였다면:

RenderTexture
      ↓ 성능 문제
Sprite Chain
      ↓ 연결부/품질 문제
Ribbon Mesh

라고 히스토리를 명시하는 게 좋아. 그렇지 않고 같은 기존 구현을 설명하는 거라면 둘 중 실제 사용했던 방식으로 통일해야 해. 면접관이 이거 보면 높은 확률로 “RenderTexture를 쓴 겁니까 Sprite Chain을 쓴 겁니까?”라고 물을 수 있어.


이 경험에서 나올 가능성이 높은 면접 질문과 답변

  1. Q. 왜 Sprite Chain 대신 Ribbon Mesh를 사용했나요?

기존 Sprite Chain 방식은 입력 경로를 따라 여러 개의 사각형 Sprite를 연결하기 때문에 방향이 급격하게 변할 경우 Sprite 사이의 연결부나 겹침이 눈에 띄는 문제가 있었습니다. 또한 객체 수가 증가하면서 개별 Sprite 관리 비용도 증가했습니다. 그래서 입력 경로를 하나의 연속된 Mesh로 구성하는 Ribbon Mesh 방식으로 변경했습니다. 경로를 기준으로 좌우 Vertex를 생성하고 Triangle로 연결해서 하나의 연속된 Stroke를 표현했습니다.

여기서 꼬리질문:

Q. Mesh라고 해서 무조건 Sprite보다 빠른가요?

아닙니다. Mesh가 항상 빠른 것은 아닙니다. Vertex 수가 지나치게 많거나 매 프레임 전체 Mesh를 재생성하면 오히려 비용이 커질 수 있습니다. 제 경우에는 다수의 개별 Sprite 객체를 관리하는 방식보다 하나의 연속 Mesh로 경로를 표현하는 것이 객체 관리와 시각적 연결 측면에서 적합해서 선택했습니다.

이 답이 중요해.
“Mesh가 무조건 Sprite보다 성능이 좋다”는 식으로 말하면 안 돼.


  1. Q. Ribbon Mesh는 어떻게 생성했나요?

터치 입력으로 중심 경로의 Point를 얻고, 각 Point에서 진행 방향을 계산했습니다. 그 진행 방향에 수직인 방향으로 일정 폭만큼 좌우 Vertex를 생성한 뒤, 이전 Point의 좌우 Vertex와 현재 Point의 좌우 Vertex를 삼각형으로 연결하여 Ribbon 형태의 Mesh를 만들었습니다.

개념적으로:

       L1 -------- L2
       |         / |
       |       /   |
       |     /     |
       R1 -------- R2

L = Left Vertex
R = Right Vertex

두 점마다:

L0, R0
L1, R1
L2, R2
...

를 만들고 삼각형으로 연결하는 구조라고 설명하면 돼.


  1. Q. 좌우 Vertex 방향은 어떻게 구하나요?

2D라면 중심점의 진행 방향이:

direction = current - previous

일 때 정규화하고, 수직 벡터를 만들 수 있어.

예를 들어:

direction = (x, y)

normal = (-y, x)

그러면:

Left  = center + normal × halfWidth
Right = center - normal × halfWidth

가 돼.

면접 답변:

입력 경로의 이전 점과 현재 점의 차이로 진행 방향을 구하고 정규화한 뒤, 그 방향에 수직인 Normal을 계산했습니다. 이후 중심점에서 Normal 방향으로 Ribbon 폭의 절반만큼 더하고 빼서 좌우 Vertex를 만들었습니다.

이 질문은 꽤 나올 만해.


  1. Q. 왜 거리 기반으로 UV를 계산했나요?

이건 네 포트폴리오에서 상당히 좋은 질문거리야.

예를 들어 Texture 하나를 Ribbon 전체에:

U = 0 ---------------------- U = 1

로 매핑하면 Stroke 길이가:

10px

이든

1000px

이든 텍스처 하나가 전체 구간을 차지하게 돼.

그러면 긴 Stroke에서는 Texture가 길게 늘어나지.

그래서 누적 거리를:

0
10
20
30
40
...

처럼 계산하고,

U = 누적 거리 / Texture 반복 길이

형태로 UV를 만들면 돼.

예를 들어 반복 간격이 100이고 Stroke 길이가 300이면:

거리     U

0     → 0
100   → 1
200   → 2
300   → 3

Texture Wrap Mode가 Repeat라면:

[Texture][Texture][Texture]

처럼 세 번 반복돼.

면접에서는:

Stroke의 전체 길이에 UV 0~1을 단순 매핑하면 Stroke 길이가 증가할수록 Texture가 늘어나기 때문에, 각 Point까지의 누적 이동 거리를 계산하고 일정 Tile Length로 나눈 값을 U좌표로 사용했습니다. 그래서 Ribbon 길이가 변해도 텍스처의 시각적 밀도를 일정하게 유지할 수 있었습니다.

이 정도가 아주 좋아.


  1. Q. UV가 정확히 뭔가요?

의외로 바로 들어올 수 있어.

UV는 Mesh의 각 Vertex가 Texture의 어느 위치를 참조할지를 나타내는 2차원 좌표입니다. 일반적으로 U가 Texture의 가로축, V가 세로축에 대응합니다. 제 Ribbon에서는 V를 좌우 방향에 0과 1로 배치하고, U를 Stroke의 누적 이동 거리에 따라 증가시켜 텍스처가 진행 방향으로 반복되도록 만들었습니다.

그러면 구조가:

Left Vertex   V = 0
────────────────────────────
 U →

Right Vertex  V = 1
────────────────────────────

라고 생각하면 돼.


  1. Q. Catmull-Rom Spline을 왜 사용했나요?

터치 입력은 매 프레임 완전히 연속적으로 들어오는 것이 아니라 일정한 간격의 샘플 Point로 들어옵니다. 이 Point를 직선으로 그대로 연결하면 입력 속도가 빠르거나 방향이 크게 변할 때 각진 Stroke가 만들어졌습니다. 그래서 인접한 Point들을 이용해 중간 경로를 보간하는 Catmull-Rom Spline을 적용했습니다.

그리고 왜 Catmull-Rom이었냐고 물으면:

Catmull-Rom은 주어진 Control Point를 실제 곡선이 통과한다는 특징이 있어서 사용자의 입력 경로를 보존하면서 중간 점을 만들기에 적합했습니다.

이 부분이 핵심이야.

Bezier와 비교하면:

Bezier
→ Control Point가 보통 곡선을 직접 통과하지 않음

Catmull-Rom
→ Control Point를 통과

그래서 사용자가 실제로 지나간 Point를 기반으로 자연스러운 경로를 만들기 좋다고 설명하면 돼.


  1. Q. Catmull-Rom은 몇 개의 Point가 필요한가요?

기본적으로 한 구간을 계산할 때:

P0, P1, P2, P3

4개의 Point를 이용해 P1 → P2 사이를 보간해.

면접 답변:

한 구간을 보간하기 위해 주변의 네 Point를 사용합니다. 이전 Point와 다음 Point까지 함께 고려하기 때문에 단순 선형 보간보다 점과 점 사이의 방향 변화가 자연스럽게 연결됩니다.

수식까지 요구하면 그때 설명하면 되지만, 게임 클라이언트 면접에서는 보통 왜 4개인지와 어떤 특성이 있는지를 이해하고 있는 게 더 중요해.


  1. Q. 그냥 Lerp를 여러 번 하면 안 됐나요?

Lerp는 두 점 사이를 직선으로 보간하기 때문에 Point 수를 늘릴 수는 있어도 기본 경로 자체는 직선입니다. 따라서 연속된 여러 Segment가 만나는 부분에서는 방향 변화가 그대로 남습니다. Catmull-Rom은 주변 Point를 함께 고려하여 곡선의 접선 방향을 결정하기 때문에 Point 사이를 더 자연스럽게 연결할 수 있습니다.

즉:

Lerp

P0 ───── P1
          \
           \
            P2

→ 꺾임 존재

반면 Spline은:

P0 ─────╮
        ╰──── P2

처럼 연결되는 거지.


  1. Q. Catmull-Rom을 적용하면 성능 비용도 증가하지 않나요?

맞습니다. 원본 입력 Point 사이에 추가 Point를 생성하기 때문에 Vertex 수와 보간 계산량이 증가합니다. 그래서 보간을 많이 할수록 무조건 좋은 것이 아니라 시각적 품질과 Vertex 수 사이의 균형을 잡아야 합니다. 실제 구현에서는 필요한 수준의 Sampling Resolution만 사용하도록 조절하는 것이 중요합니다.

여기서 한 단계 더 좋은 답:

사용자 입력 속도나 Point 사이 거리 등을 기준으로 Sampling 밀도를 동적으로 조절하는 것도 개선 방법이라고 생각합니다.

주요 활용 분야:

  • 컴퓨터 그래픽스 및 애니메이션: 캐릭터의 이동 경로, 물체의 부드러운 트랙킹 등을 구현할 때 사용합니다.
  • 게임 개발: 카메라가 일련의 키프레임(Keyframe)을 따라 물흐듯 부드럽게 이동하게 하거나, 유도탄/점프 등의 이동 궤적을 계산할 때 활용합니다.
  • 2D/3D 벡터 그래픽: 마우스나 펜으로 입력한 불연속적인 좌표들 사이를 매끄럽게 연결하는 선을 그릴 때 쓰입니다

  1. Q. RenderTexture와 Ribbon Mesh의 근본적인 차이는 무엇인가요?

RenderTexture 방식은 입력 결과를 Texture의 Pixel에 기록하는 이미지 기반 방식이고, Ribbon Mesh는 입력 경로를 Vertex와 Triangle로 구성하여 Geometry로 표현하는 방식입니다. RenderTexture에서는 텍스처 해상도나 반복적인 픽셀 갱신 비용의 영향을 받을 수 있는 반면, Ribbon Mesh는 Stroke 복잡도에 따라 Vertex 수와 Mesh 갱신 비용이 증가하는 특성이 있습니다.

이 질문에서는 한쪽이 무조건 좋다고 하면 안 돼.

RenderTexture
비용 → 해상도 / 픽셀 처리 / 갱신 범위 영향

Ribbon Mesh
비용 → Point / Vertex / Triangle 수 영향

이라는 trade-off를 알고 있는 게 중요해.


  1. Q. 그러면 왜 항상 Ribbon Mesh를 사용하지 않나요?

요구사항에 따라 다릅니다. 단순한 그림판처럼 Stroke를 최종 이미지로 누적시키는 것이 목적이면 RenderTexture가 효율적일 수 있습니다. 반면 제 프로젝트처럼 Stroke 자체에 반복 Texture를 적용하고 곡선의 형태나 폭을 실시간으로 제어해야 하는 경우에는 Mesh 방식이 더 유연했습니다.

이 답이 좋아.

기술 선택은:

A가 B보다 좋다

가 아니라:

“현재 요구사항에서 A의 장점이 더 적합했다.”

라고 설명하는 게 중요해.


  1. Q. 실시간으로 Mesh를 계속 변경하면 GC가 발생하지 않나요?

이 질문은 성능 최적화 프로젝트라면 꽤 위험하면서도 좋은 질문이야.

답은 구현에 따라 달라지지만 원칙적으로는:

Vertex나 UV List를 매 프레임 새로 생성하거나 배열을 반복적으로 할당한다면 GC가 발생할 수 있습니다. 그래서 실시간 Mesh 생성에서는 List나 Buffer를 재사용하고, 충분한 Capacity를 미리 확보하거나 필요한 부분만 갱신하는 방식으로 런타임 할당을 최소화하는 것이 중요합니다.

실제 네 구현이 매번 new List를 했다면 “저는 재사용했습니다”라고 하면 안 되고, 이렇게 답해야 해:

당시 구현에서는 실시간 Mesh 방식 자체를 우선 구현했고, 추가 최적화가 필요하다면 Vertex/UV Buffer 재사용과 Capacity 사전 확보를 적용할 수 있습니다.

실제 코드보다 좋게 말하면 안 돼.


  1. Q. Sprite Chain 방식의 단점은 객체 수 말고 무엇이 있나요?

곡선에서 각 Sprite의 회전값이 달라지면서 연결부가 벌어지거나 겹칠 수 있고, 급격한 방향 전환에서는 직사각형 형태가 드러나면서 Stroke가 각져 보일 수 있습니다. 이를 감추기 위해 Sprite를 더 촘촘하게 생성하면 반대로 객체 수와 갱신 비용이 증가하는 trade-off가 생깁니다.

이건 네 그림하고 딱 연결돼.

Sprite 적게 사용
→ 성능 ↑
→ 연결부 품질 ↓

Sprite 많이 사용
→ 품질 ↑
→ 객체 수 / 갱신 비용 ↑

Ribbon Mesh로 바꾼 이유를 아주 잘 설명할 수 있어.


  1. Q. 다중 터치가 되면 Ribbon Mesh는 어떻게 각각 구분했나요?

이건 네 다른 핵심 경험하고 연결돼.

각각의 Touch ID를 Key로 독립적인 Touch Context를 관리했습니다. Context마다 현재 Stroke의 입력 Point와 Ribbon Mesh 인스턴스를 별도로 가지고 있기 때문에 여러 Touch가 동시에 들어와도 서로의 Mesh를 수정하지 않도록 구성했습니다.

개념적으로:

Touch ID 1
→ Points 1
→ Ribbon Mesh 1

Touch ID 2
→ Points 2
→ Ribbon Mesh 2

Touch ID 3
→ Points 3
→ Ribbon Mesh 3

이걸 하나의 전역:

currentPoints
currentMesh

로 처리하면 여러 손가락이 동시에 입력될 때 상태가 뒤섞여.

따라서 이건 단순히 “멀티터치를 지원했다”보다:

Touch ID 단위의 독립된 상태 컨텍스트를 설계했다

고 설명하는 게 훨씬 좋다.


  1. Q. 성능 최적화 효과는 어떻게 검증했나요?

여기서는 실제 측정한 범위만 말해야 해.

현재 네 경험에 맞춰서는:

초기 구현에서 다중 터치가 증가했을 때 프레임 저하를 확인했고, Sprite/Object 관리 방식과 드로잉 렌더링 구조를 개선한 뒤 실제 목표 전자칠판 환경에서 다중 터치 테스트를 수행했습니다. Object Pooling, 점진적인 객체 회수, Ribbon Mesh 기반 Rendering 등을 적용한 이후 목표인 약 60 FPS 수준을 안정적으로 유지할 수 있는지 확인했습니다.

그리고 면접관이:

“Profiler 수치는?”

라고 물었는데 정확한 수치를 기록하지 않았다면 솔직하게:

“당시에는 Cocos Creator 2.4.x에서 제공하는 내장 프로파일러로 단순한 실시간 수치 통계만을 확인했습니다. 심층적인 성능 분석이 필요한 경우 외부 도구를 함께 활용할 수 있으나 해당 부분까지는 확인하지 않았습니다."


먼저 1분 답변은 이렇게 가져가면 좋아.

“전자칠판에서 여러 사용자가 동시에 그림을 그릴 때, 터치 경로를 따라 Sprite를 이어 붙이는 방식은 곡선에서 연결부가 드러나고 객체 수가 늘어나는 문제가 있었습니다. 그래서 입력 경로를 하나의 연속된 Ribbon Mesh로 생성하는 방식으로 변경했습니다.
터치 Point는 Catmull-Rom Spline으로 보간해 부드러운 중심 경로를 만들고, 각 Point에서 진행 방향에 수직인 벡터를 계산해 좌우 Vertex를 생성했습니다. 인접한 Vertex는 두 개의 Triangle로 연결해 Ribbon 형태의 Mesh를 만들었습니다.
또 Stroke의 누적 이동 거리를 기준으로 UV의 U값을 계산하고 Texture Repeat를 적용해, 선이 길어져도 모래 텍스처가 늘어나지 않고 일정한 밀도로 반복되도록 했습니다. 입력 Point는 최소 이동 거리를 기준으로 Sampling하고 기존 Vec2, Vec3 배열을 재사용해서 불필요한 객체 생성도 줄였습니다.”

이 답변 다음에는 아래 정도를 준비하면 돼.

  1. Q. 왜 Sprite Chain이 아니라 Ribbon Mesh를 사용했나요?
    A. Sprite Chain은 경로를 따라 여러 사각형 Sprite를 배치하기 때문에 급격한 방향 전환에서 Sprite 사이의 겹침이나 연결부가 드러날 수 있고, Stroke가 길어질수록 관리해야 하는 객체도 증가합니다. Ribbon Mesh는 전체 경로를 하나의 연속된 Geometry로 표현할 수 있어서 연결부 표현을 개선하고, Vertex와 Triangle 단위로 경로를 제어하기에도 적합했습니다.
  2. Q. Mesh가 Sprite보다 무조건 빠른 건가요?
    A. 아닙니다. Mesh도 Vertex 수가 너무 많거나 매번 전체 Mesh를 갱신하면 CPU와 GPU 비용이 증가합니다. 제 경우에는 개별 Sprite 객체를 많이 관리하는 비용과 곡선의 연결 문제를 해결하는 데 Ribbon Mesh가 더 적합했습니다. 요구사항에 따른 선택이지 Mesh 자체가 항상 더 빠른 것은 아닙니다.
  3. Q. Ribbon Mesh를 구체적으로 어떻게 만들었나요?
    A. 보간된 중심 경로의 각 Point에서 다음 Point와의 차이로 진행 방향을 구하고 정규화했습니다. 그 방향의 수직 벡터 (-dirY, dirX)를 구한 뒤 중심점에서 Ribbon 폭의 절반만큼 더하고 빼 좌우 Vertex를 만들었습니다. 그리고 인접한 두 Point의 좌우 Vertex 네 개를 두 개의 Triangle로 연결했습니다.
  4. Q. 왜 수직 벡터를 (-y, x)로 만들 수 있나요?
    A. 2차원 벡터 (x, y)를 90도 회전시키면 (-y, x)가 됩니다. 진행 방향과 이 벡터의 내적은 0이므로 서로 수직입니다. 그래서 Ribbon의 진행 방향과 직각인 폭 방향을 만들 때 사용할 수 있습니다.
  5. Q. 왜 Catmull-Rom Spline을 사용했나요?
    A. 터치 입력은 연속된 선이 아니라 일정 간격으로 들어오는 Sample Point이기 때문에 그대로 직선 연결하면 빠르게 움직이거나 방향이 바뀔 때 각진 형태가 나타났습니다. Catmull-Rom은 주변 Point를 이용해 부드러운 중간 경로를 만들면서 실제 Control Point를 통과하기 때문에 사용자 입력 경로를 유지하는 데 적합했습니다.
  6. Q. Catmull-Rom에는 왜 네 개의 Point가 필요한가요?
    A. P1에서 P2 사이의 곡선을 계산할 때 앞뒤의 P0, P3까지 이용해 두 끝점의 접선 방향을 결정하기 때문입니다. 그래서 단순히 두 점만 사용하는 Lerp보다 구간 사이의 방향 변화가 자연스럽게 이어집니다.
  7. Q. Lerp를 여러 번 쓰면 안 되나요?
    A. Lerp는 기본적으로 두 Point 사이를 직선으로 보간하기 때문에 Point 수를 늘려도 각 Segment의 경계에서 방향이 급격히 변할 수 있습니다. Catmull-Rom은 앞뒤 Point까지 고려해 접선을 구성하므로 연속된 곡선을 표현하기에 더 적합했습니다.
  8. Q. SMOOTH_SEGMENT_STEPS = 4는 무엇인가요?
    A. 원본 Point 사이 한 Segment를 몇 단계로 나눠 보간할지 결정하는 값입니다. 값을 늘리면 곡선은 부드러워지지만 생성되는 Point와 Vertex 수, 계산량도 증가합니다. 그래서 시각적 품질과 성능 사이의 trade-off 값입니다.
  9. Q. 거리 기반 UV Tiling을 왜 사용했나요?
    A. Ribbon 전체 길이에 UV 0~1만 매핑하면 Stroke가 길어질수록 텍스처가 늘어납니다. 그래서 이전 Point부터 현재 Point까지의 거리를 누적하고 누적 거리 / tileLength를 U값으로 사용했습니다. Texture의 WrapMode를 Repeat로 설정했기 때문에 U가 1, 2, 3으로 증가할 때마다 텍스처가 일정한 길이 단위로 반복됩니다.
  10. Q. UV의 U와 V는 코드에서 각각 무엇을 의미하나요?
    A. U는 Stroke의 진행 방향이고 V는 Ribbon의 폭 방향입니다. 왼쪽 Vertex는 V=1, 오른쪽 Vertex는 V=0으로 두고, U는 누적 이동 거리에 따라 계속 증가하도록 만들었습니다.
  11. Q. RenderTexture 방식인가요?
    A. 아닙니다. 이 구현은 cc.Mesh와 cc.MeshRenderer를 사용해서 Vertex, UV, Index를 직접 갱신하는 Geometry 기반 Ribbon Mesh 방식입니다. Texture2D는 Mesh 표면에 입힐 모래 질감일 뿐 RenderTexture에 픽셀을 누적하는 구조는 아닙니다.
  12. Q. 실시간으로 Mesh를 갱신하면 GC가 많이 발생하지 않나요?
    A. 그 가능성이 있어서 smoothPoints, positions, uvs의 기존 Vec2/Vec3 객체를 재사용하도록 구현했습니다. 필요한 Capacity가 부족할 때만 추가 객체를 만들고, 이미 확보된 객체는 값만 갱신합니다. 다만 indices.length = 0 후 다시 push하는 부분이나 엔진 내부 setVertices 구현까지 포함해 완전한 Zero-GC라고 주장할 수는 없고, 정확한 할당량은 Profiler로 확인해야 합니다.
  13. Q. 입력 Point를 왜 8픽셀 이상 움직였을 때만 추가했나요?
    A. 미세한 손 떨림이나 지나치게 촘촘한 터치 이벤트를 전부 Point로 저장하면 Spline Point와 Vertex 수가 빠르게 증가합니다. 그래서 최소 이동 거리를 두어 입력 자체를 Sampling했습니다. 이를 통해 불필요한 Geometry 증가를 제한했습니다.
  14. Q. 코드에 30FPS Mesh Throttling도 있던데 정확히 30FPS로 제한되나요?
    A. 현재 코드만 보면 정확히 그렇지는 않습니다. 앞에서 8픽셀 미만 이동은 이미 return하는데 Mesh 갱신 조건은 7픽셀 이상 이동 OR 1/30초 경과라서, moveStroke가 다음 단계까지 온 경우 거리 조건이 대부분 먼저 참이 됩니다. 그래서 현재 구현의 실제 효과는 30Hz 제한보다는 거리 기반 Sampling이 더 큽니다. 다시 개선한다면 시간 기반 갱신을 우선하거나 조건을 분리해 의도한 Throttling이 명확하게 동작하도록 수정하겠습니다.

이 질문은 오히려 꼭 준비해두는 게 좋아. 코드를 자세히 보는 면접관이면 발견할 수 있어.

  1. Q. 왜 OR 대신 AND로 바꾸면 안 되나요?
    A. AND를 사용하면 충분히 이동했고 동시에 최소 시간이 지나야 갱신되기 때문에 확실한 빈도 제한이 됩니다. 대신 빠르게 큰 거리를 움직여도 최대 약 30Hz로만 화면에 반영돼 입력 반응성이 떨어질 수 있습니다. 그래서 목적에 따라 “시간 기준”, “거리 기준”, “둘의 조합”을 결정해야 합니다.
  2. Q. 현재 코드는 매번 전체 Mesh를 다시 계산하는 것 아닌가요? 더 최적화할 수 있나요?
    A. 맞습니다. updateMesh()에서 현재까지의 전체 Smooth Point를 다시 순회하고 있습니다. Stroke가 길어질수록 갱신 비용이 증가하는 구조입니다. 개선한다면 새롭게 추가된 Segment 부분만 Vertex와 Index Buffer에 append하거나, 일정 길이가 지나면 Mesh를 chunk 단위로 분할하여 이미 완성된 구간은 더 이상 갱신하지 않는 방식이 가능합니다.

이건 굉장히 좋은 고급 꼬리질문이야.

    1. Q. Ribbon의 급격한 코너에서 폭이 이상해지지 않나요?
      A. 현재 구현은 각 Point에서 하나의 Normal을 이용해 좌우 Vertex를 단순 생성하기 때문에 아주 급격한 코너에서는 안쪽이 겹치거나 바깥쪽 폭이 부자연스러울 수 있습니다. 더 정교하게 구현한다면 이전 Segment와 다음 Segment의 Normal을 평균낸 Join 방향을 사용하거나 Miter, Bevel, Round Join 같은 방식으로 코너를 처리할 수 있습니다.
    2. Q. Stroke의 시작점과 끝점은 어떻게 처리하나요?
      A. 현재 구현에서는 중심 경로의 마지막 Point에서 이전 Segment 방향을 사용해 좌우 Vertex를 만드는 단순한 끝 처리입니다. 별도의 Cap Geometry는 없습니다. 시각적으로 더 완성도 있게 만들려면 Round Cap이나 별도의 시작/끝 Mesh를 추가할 수 있습니다.
    3. Q. Catmull-Rom을 쓰면 원래 입력 경로에서 너무 튀어나가는 경우는 없나요?
      A. 가능합니다. 특히 Point 간격이 불균일하거나 방향 변화가 큰 경우 일반 Catmull-Rom에서 overshoot가 생길 수 있습니다. 필요하다면 입력 Point 간격을 일정하게 resampling하거나, Centripetal Catmull-Rom처럼 overshoot가 덜한 변형을 사용하는 방법을 고려할 수 있습니다.
    4. Q. 다중 터치에서는 이 Ribbon 하나를 어떻게 관리했나요?
      A. 각 Touch ID별로 독립된 Stroke/Ribbon 인스턴스를 연결하는 구조로 관리했습니다. 하나의 전역 rawPoints를 여러 터치가 공유하게 하면 서로의 입력이 섞이기 때문에, Touch ID를 Key로 개별 Context와 Ribbon을 연결해 각 사용자의 Start → Move → End 생명주기를 독립적으로 처리했습니다.
    5. Q. Object Pooling하고 이 Ribbon Mesh가 어떤 관계가 있나요?
      A. Ribbon 생성 방식과 Pooling은 별개의 최적화입니다. Ribbon Mesh는 Stroke의 렌더링 구조를 개선한 것이고, Object Pooling은 반복적으로 생성되는 게임 오브젝트와 이펙트의 Instantiate/Destroy 및 메모리 할당 비용을 줄이기 위한 것입니다. 서로 다른 병목을 해결한 최적화라고 설명할 수 있습니다.
    6. Q. 성능이 좋아졌다는 걸 어떻게 검증했나요?
      A. 당시에는 Cocos Creator 2.4.x의 내장 프로파일러를 이용해 실제 전자칠판 환경에서 Framerate, Draw Call, Frame Time, Game Logic, Renderer 같은 실시간 통계를 확인했습니다. 특히 다중 터치 상황에서 최적화 전에는 눈에 띄는 프레임 저하와 끊김이 있었는데, Ribbon Mesh와 입력 샘플링, 객체 재사용 등의 최적화를 적용한 뒤 실제 목표 장비에서 약 60FPS가 유지되는 것을 확인했고, 동시에 사용자가 직접 드로잉했을 때의 끊김이나 반응성도 육안으로 확인했습니다. 다만 당시에는 내장 프로파일러를 이용한 실시간 지표와 실제 장비 테스트를 중심으로 검증했고, CPU/GPU별 병목이나 GC Allocation을 외부 프로파일러로 세밀하게 전후 비교해 수치화하지는 못했습니다. 다시 진행한다면 Chrome DevTools나 플랫폼별 GPU/메모리 프로파일링 도구를 병행해서 Frame Time, GC, Draw Call, Vertex 수 등을 변경 전후로 기록할 것 같습니다.”

“그럼 정말 Ribbon Mesh 때문에 빨라졌다는 걸 증명할 수 있나요?”

“엄밀하게는 여러 최적화를 함께 적용했기 때문에 Ribbon Mesh 하나의 개선 효과를 독립적으로 정량화했다고 말할 수는 없습니다. 당시에는 실제 타깃 장비에서 다중 터치 시 발생하던 프레임 저하를 재현하고, 개선 후 내장 Stats에서 목표 프레임을 유지하는 것과 체감 끊김이 사라진 것을 기준으로 검증했습니다. 보다 정확한 비교를 한다면 각 최적화를 하나씩 적용하는 A/B 테스트와 Frame Time 측정을 통해 기여도를 분리했을 것 같습니다.”

마지막으로 면접에서 제일 좋은 흐름은 **“Ribbon Mesh가 무조건 좋은 기술이라서 썼다”**가 아니라 이렇게 말하는 거야.

“다중 터치 환경에서 기존 방식의 객체 관리와 연결부 표현이 요구사항에 맞지 않았고, Stroke를 하나의 연속 Geometry로 제어해야 했기 때문에 Ribbon Mesh를 선택했습니다. 대신 Vertex 수와 Mesh 갱신 비용이라는 새로운 trade-off가 생겼기 때문에 입력 Sampling과 데이터 재사용을 같이 적용했습니다.”

면접에서 세 기술을 연결해서 설명한다면

이 답변 하나로 꽤 많은 걸 커버할 수 있어.

“전자칠판의 터치 입력은 일정 간격의 Point로 들어오기 때문에 그대로 연결하면 Stroke가 각지게 표현됐습니다. 그래서 주변 네 Point를 참고하여 실제 입력 Point를 통과하는 Catmull-Rom Spline으로 중간 경로를 보간했습니다.

보간된 중심 경로에서는 진행 방향에 수직인 벡터를 구해 좌우 Vertex를 만들고 Triangle로 연결하여 Ribbon Mesh를 생성했습니다. 텍스처는 전체 Ribbon에 단순히 0~1 UV를 Stretch하지 않고, 각 Point까지의 누적 이동 거리를 Tile Length로 나눈 값을 U좌표로 사용해 Stroke의 길이에 관계없이 일정한 크기로 반복되도록 했습니다.

그리고 터치 입력과 Mesh Vertex가 서로 다른 좌표 공간을 사용할 수 있기 때문에 World 좌표의 입력을 convertToNodeSpaceAR로 Ribbon Node의 Local 좌표로 변환한 후 Mesh를 생성했습니다. 이후 Local에서 실제 화면까지의 변환은 Cocos의 렌더링 파이프라인이 처리하도록 했습니다.”

이 정도 이해하고 있으면 단순히 “Catmull-Rom을 가져다 썼다” 수준이 아니라, 왜 필요한지 → 어떻게 Mesh가 되는지 → Texture는 어떻게 붙는지 → 좌표는 왜 변환하는지까지 하나의 파이프라인으로 설명할 수 있어.