1. 멀티 에이전트 시스템

LLM의 본질

  • LLM은 확률 기반 다음 단어 생성기
  • 컨텍스트 = 전체 대화 기록 (대화가 핑퐁될수록 맥락 누적)
  • LLM은 텍스트만 출력 가능 (JSON 포함)

프롬프트 설계 원칙

나쁜 예: 작성+검사 프롬프트 120줄
좋은 예: 작성 60줄 + 검사 60줄

  • 작성 시점에는 검사 프롬프트가 지침 정도로만 기능
  • 작성 완료 후 검사 프롬프트가 제대로 된 피드백 제공
  • 각 단계마다 필요한 프롬프트만 포함

피드백 루프

  • 한 번에 완벽한 결과 X
  • 작고 빠른 루프를 반복하는 것이 효율적
  • 각 서브 에이전트마다 작업 가이드 배치
  • 구조: 오케스트레이터 / 서브에이전트 (예: FE 구현자/검토자)

개발 과정의 진화

CLI → 데스크톱 앱 → 컨텍스트 윈도우 문제 발생 → JSON 템플릿화

핵심 정리

  1. 논리적으로 압축하기
  2. 세부 스케치는 뒤에서 (같은 문서 공유)
  3. 같은 철학, 같은 가치관 유지
  4. 텍스트 양이 많아지는 순간 최대한 지연
  5. 자료구조 활용
  6. 프로그래밍 입출력 활용

2. 요즘 회사 일

변화한 업무 속도

  • 2~3년 전 대비 피처 개발 시간이 크게 단축
  • 회사마다 AI 수용 정도와 속도 차이가 큼

시니어 개발자의 하루

  • AI를 통해 개발 진행도만 빨라짐 (코드 작성, 리뷰, 테스트)
  • PM, 디자이너는 상대적으로 느림 (사람 중심 업무)
  • 결과: 기다리는 공백 시간 발생

남는 시간 활용법

1) AI 에이전트/도구 개발

  • 김재섭 mk.2
  • 사내용 네트워크 디버거 (Proxyman 같은 도구를 사내 워크플로우에 맞게 구현)
  • FE 의존성 업데이트 에이전트 (반복적 업그레이드 자동화, PR 자동 생성)
  • 로우 시큐(?) or Obsidian으로 작업 내용, 회의록을 맥락으로 정리

2) 제품에 기여

문제 정의 (우선순위)

  • 비즈니스 기회 / 성과 / UX 임팩트 관점
  • 어떤 기능이 일정을 폭발시킬지 판단

본질부터 (대안 제안)

  • PRD의 "왜 필요한지"를 먼저 질문
  • 더 효율적인 대안 제안 (대부분 거절돼도 OK)

지표 설계 (다음 개선)

  • 지표 대시보드 직접 설계
  • 운영 이슈를 다음 제품 개선 아이디어로 전환

PM은 직책이 아니라 역할이다

3) AI 잘 쓰는 법

  • 사내 스터디 진행
  • 반복 가능한 컨텍스트로 작업 안정화
  • 어떤 일을 에이전트/사람이 할지 잘 설계
  • 노하우를 팀으로 공유해서 전체 수준 끌어올리기

AI에게 일을 시키는 능력도 차별점이다

4) 역할 변화

  • 기존: 구현 업무를 직접 손으로
  • 현재: AI와 페어로 작업
  • 사람의 역할: 판단, 검증, 아키텍처 결정

AI가 코드를 짜줘도 그 코드를 책임지는 건 결국 나다

학생/저연차 개발자를 위한 조언

  • AI로 비즈니스 대박 노리지 말기
  • 정말 작은 문제를 한 번 풀어보기
  • 무얼 할지 방향 잡고, 작더라도 성공 경험 쌓기
  • 큰 성공보다 끝까지 풀어본 작은 문제가 다음 방향을 만든다

인디 개발의 현실

  • A/B 테스트를 정말 빠르게 진행
  • "돈 벌고 유저들한테 사랑받는 제품" → 진짜 어려움

앞으로 중요한 것

  1. 무엇을 실행할지 고르는 능력
  2. 고른 것을 실제로 성공시키는 힘

3. 신입 개발자 필요한가요?

AI의 한계

  • AI는 "가챠" (비결정적)
  • 예: 같은 코테를 같은 프롬프트로 맡겨도 매번 다른 결과

개발자 일이 줄어드는가?

줄지 않은 일:

  • 요구사항 분석
  • 리서치
  • 아키텍처 인터페이스 설계
  • 코드 설계
  • 테스트/QA

변화:

  • 한 번에 진행하는 프로젝트 개수 증가
  • 그만큼 더 많이 일함

회사가 신입에게 바라는 것

"했습니다" ❌
"왜" 했는지를 구체적으로 설명

학생이 해야 하는 것: TF 리더 연습

1) 문제부터 정의

  • "무엇을 만들지"가 아니라 "누구의 어떤 문제를 풀지"

2) AI 결과를 의심

  • "짜줘" ❌
  • "옵션 3개로 비교해줘" ✅

3) 기본기를 더 깊게

AI를 평가하려면 기준이 있어야 함:

  • CS, 알고리즘, 자료구조
  • 쓰는 기술의 동작 원리
  • OS, 네트워크, DB 동작 원리
  • 요구사항 분석
  • 디자인 패턴, 클린 코드

4) 여정을 설명하기 - 5요소

프로젝트를 다음 순서로 설명할 수 있는가?

  1. 배경 - 누구의 어떤 문제였나?
  2. 임팩트 - 그 문제가 얼마나 풀렸나? (지표, 반응)
  3. 대안 비교 - 다른 안과 무엇을 따져 골랐나?
  4. 동작 원리 - 어떻게 돌아가나?
  5. 사이드 이펙트 - 어떤 케이스에서 어떻게 처리했나?

4가지를 훈련하고 하나라도 TF 리더처럼 행동해보자!


4. 눈물의 AI 격변기

  • 링크드인 활용
  • 넓고 얕게 경험하기

5. 경쟁력 있는 개발자는?

단순 코딩 ❌ 소프트웨어 개발자 ⭕

  • 기술적으로 코드 깎는 장인보다 전체를 보는 E2E Engineer
  • 개발자 한 명의 파트/역할이 늘어남
  • "좋은 기술 만들기" → "좋은 경험 만들기"

안목 높이기

AI는 인형 뽑기 기계
뽑는 사람이 어떻게 뽑느냐?

피해야 할 세 가지

1) 인지 부채

AI에 지나치게 의존, 스스로 사고하지 않음

2) 지적 비만

너무 많은 정보를 섭취해서 실제로 소화하지 못함

3) 인지 중력

편해진 방법에서 벗어나는 것이 어려움


AI에 너무 의존하지 말라는 내용을 AI를 써서 요약했다.

2026-05-12 18:10:02.438 22875-22902 GoogleApiManager com.example.got_you_studying E Failed to get service from broker. java.lang.SecurityException: Unknown calling package name 'com.google.android.gms'. at android.os.Parcel.createExceptionOrNull(Parcel.java:3340) at android.os.Parcel.createException(Parcel.java:3324) at android.os.Parcel.readException(Parcel.java:3307) at android.os.Parcel.readException(Parcel.java:3249) at bjcn.a(:com.google.android.gms@261631035@26.16.31 (260400-900800821):36) at bjaj.z(:com.google.android.gms@261631035@26.16.31 (260400-900800821):143) at bigf.run(:com.google.android.gms@261631035@26.16.31 (260400-900800821):42) at android.os.Handler.handleCallback(Handler.java:995) at android.os.Handler.dispatchMessage(Handler.java:103) at czrc.mD(:com.google.android.gms@261631035@26.16.31 (260400-900800821):1) at czrc.dispatchMessage(:com.google.android.gms@261631035@26.16.31 (260400-900800821):5) at android.os.Looper.loopOnce(Looper.java:248)

 

안드로이드 스튜디오를 써서 과제를 하는 중, 파이어베이스가 제대로 작동하지 않았다.

 

파이어베이스 콘솔에 등록도 했고, 경로에 넣으라는 json파일도 넣고,,

다 바꿨지만 데이터베이스에 접근을 할 수가 없었다.

 

알아보니 몇가지 문제점이 있었지만

그중에는 일반적인것도 있고 맥북이라서 나는 문제도 있었다.

 

크게 나누자면 문제는 3가지였다.

1. 에뮬레이터의 Services 설정 확인

2. 네트워크 설정

3. 콘솔에서 "규칙" 설정

 


1.에뮬레이터의 버전, services 확인

    22902 GoogleApiManager	com.example.got_you_studying	E  Failed to get service from broker.

가장 처음에 떴던 에러다. 파이어베이스에 접근을 아예 못하고있는 듯 했다.

저 로그를 검색해보면 설치된 에뮬레이터의 api의 버전 문제라고 한다.

 

어느정도 검색해봤다면 버전은 무조건 맞추었을 테니까, 버전 다음으로 중요한 것은 저 play 쪽에 플레이 스토어 마크가 있는지 봐야한다.

 

설치한 에뮬레이터에 System Image를 설치해줘야하는데, 이부분을

Google APIs ARM 64 v8a System Image가 아닌

Google Play ARM 63 v8a System Image를 설치해줘야했다.

 

 

 

 

그런데 혹~시 아래처럼 Services를 펼쳤을때에 Google Play Store 가 아예 없을 수도 있다.

있음 / 없음

 

 

 

 

그럴때에는 처음 에뮬레이터에  Add Device를 할 때, 저 Play 칸에 플레이 마크가 있는지를 봐야한다.

마크가 달려있는 가상기기만 구글 플레이 스토어로 이미지를 설치할 수 있었다.

 

이렇게 에뮬레이터 설정을 하고 cold boot를 하면 아까 그 에러는 뜨지 않았다.


2. 네트워크 설정

java.net.UnknownHostException: Unable to resolve host "firestore.googleapis.com": No address associated with hostname

이건 윈도우os의 기기에서는 없고, 맥북에서 실행한 안드로이드 스튜디오에서 돌리는 에뮬레이터에서만 있는 문제같았다.

 

한마디로 에뮬레이터가 인터넷에 연결을 못하고있는 문제였다.

실제로 에뮬레이터 안에서 구글에 들어가려고 해도 네트워크가 없어서 들어갈 수 없었다.

 

꽤 유명한 문제 같았다.

아래의 스택오버플로우에 자세히 잘 나와있다.

https://stackoverflow.com/questions/50670547/android-studio-android-emulator-wifi-connected-with-no-internet

보면 두가지 방법이 있는데, 둘 다 환경에따라 되기도하고 안되기도 하는 듯 하다.

1.맥북의 네트워크를 끈다 -> 노트북 재부팅 -> 에뮬레이터 wipe data 후 cold boot ->

에뮬 네트워크 연결(안됐다고 뜨는데 사실은 된거라고 함) -> 노트북 와이파이 연결

 

2.맥북의 설정에 DNS 검색 -> DNS 서버 메뉴 진입 -> IPv4또는 IPv6주소에 직접 [8.8.8.8]을 추가

 

난 처음에 2번방법은 귀찮을 것 같아서, 그리고 오랫동안 끈 적이 없던 노트북을 재부팅할 겸 1번방법을 했다.

그렇게 3번정도를 반복했는데 안됐다.

 

2번방법이 훨씬 쉽고 간단했다. 아까 그 에러는 이제 안떴다.

 


3. 콘솔에서 규칙 설정

이건 아주 간단했다.

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

저 write: if true 부분이 false로 돼있으면 권한이 없어서 에러가 난다.

true로 바꿔주면 에러가 안난다.

 

이러면 이제 잘 작동한다!

외적,내적,,워낙 많이 들어본 두 이름이다.
두가지가 어떤 경우에 어떻게 쓰인다~ 까지는 암기하듯 외우고 있었다.
이번에 내적과 외적에 대해 찝찝하고 궁금했던것들을 제대로 공부해보았다.
혹시모를 나같은 완전 수포자분들에게 도움이될까 부족하지만 적어본다...

내적과 외적에 대해 적기 전에,

벡터(Vector)

벡터란 단순히 이야기하면 숫자를 원소로 가지는 배열이다.
이것을 사용해 하나의 숫자로는 표현이 어려운 것 들을 표현할 수 있다.

크기,방향,회전,힘,속도 등 다양한 양 을 표현할 수 있다.
게임 프로그래밍에서는 Position,Rotation,Scale도 Vector로 표현한다.

예시)
2D의 경우 A.position = new Vector2(1,0);
3D의 경우 B.position = new Vector3(1,0,3);

2D게임에서 Player의 position이 (2,3) , Enemy1의 position이 (4,1) 이라고 할때
그래프에 아래처럼 위치할 수 있다.

그래프에서의 내적

그래프, 게임응용에 가기 전에 단순히 공식으로만 먼저 봐보자.
단순히 내적을 구하는 "공식"은 (x의곱)+(y의곱) 이다.
그렇다면 위의 Player와 Enemy1의 경우에는 (2x4)+(3X1) 이니까 내적값은 11이 된다.

그래프로 그려본다면 이렇다.

여기서 Player와 Enemy1의 벡터값을 (0,0)으로부터 이은 선이 만나는 각도가 보인다.
한번 다른 점도 찍어볼까?

마찬가지로 여러개의 Enemy를 배치해 보았다.
Player(2,3)을 기준으로 위의 공식으로 내적값을 구했을때,

Enemy1(4,1)의 내적값은 11(양수)이고 예각을 나타낸다.
Enemy2(3,-2)의 내적값은 0이고 직각을 나타낸다.
Enemy3(-4,-1)의 내적값은 -11이고 둔각을 나타낸다.

아~ 내적값이 양수면 예각, 0이면 직각, 음수면 둔각이 되는구나~

...라고 이해 해 볼 수 있다.

방향벡터

방향 벡터란 쉽게말해서 객체가 바라보고 있는 방향을 나타내는 벡터다.
플레이어가 (5,7)방향으로 바라보고 있다면, 방향벡터는 (5,7)이다.

하지만 이 방향벡터는 단순히 방향만 나타내고있지 않다.
(5,7)은 방향뿐 아니라 크기를 나타내기도 한다. 이게 무슨뜻이나면


(5,7)과 (5.81,8.14)와 (6.98,9.77)은 크기가 다르지만 (0,0)을 기준으로 같은 방향으로 뻗어나가고있는 벡터다.

계산할때의 편의성과 일관성, 물리적 의미를 위해서 방향만을 표현하는 벡터를 구하고싶다.
순수한 방향만을 나타내는 방향벡터는 어떻게 구할 수 있을까?

이 때 정규화 라는걸 해주면 딱 방향만 나타내는 벡터를 구할 수 있다.
정규화란 한 벡터의 방향은 유지하지만 크기는 1이 되도록 다시 계산하는 것이다.
(값이-1 ~ 1이다.)

(5,7)과 (5.81,8.14)와 (6.98,9.77)은 정규화 하면 (0.58, 0.81)이 된다.
이 값은 유니티에서 Nomalize함수를 사용해 쉽게 얻을 수 있다.

정규화된 값(방향)에 힘을 구하면 객체의 속도를 구할 수 있다.

게임에서의 내적

이제 드디어 게임에서의 내적을 알아보기위한 재료가 다 모였다.

게임에서 벡터의 내적은 바라보는 방향을 구하는데에 응용된다.
Enemy가 Player를 바라보고있는지, 다른곳을 보고있는지 벡터의 내적을 사용해 구할 수 있다.

  1. Player와 Enemy1의 위치 벡터를 구한다.
Vector3 playerPosition = player.transform.position;
Vector3 enemy1Position = enemy1.transform.position;
  1. Player와 Enemy1사이의 방향 벡터(위에서 말한 정규화 된 값)을 구한다.
Vector3 direction = (playerPosition - enemy1Position).normalized;
  1. Enemy1이 바라보고있는 방향의 방향벡터를 구한다.
Vector3 enemyForward = enemy.transform.forward;
  1. 유니티의 Dot함수를 사용해 내적 계산을 해보자.
float dotProduct = Vector3.Dot(enemyForward, direction);
if (dotProduct > 0)
{
    // 내적값이 양수다.
}
else if (dotProduct < 0)
{
   // 내적값이 음수다.
}
else 
{
   // 내적값이 0이다.
}

지금은 검은선(player와enemy1의 방향벡터) 과
빨간선(enemy1이 바라보는방향) 이 예각을 띄고있다.

아까 빨간옷 입은 친구가 한 말을기억해보자.

내적값이 양수면 예각, 0이면 직각, 음수면 둔각

Enemy1의 시선의 방향벡터가 빨간 선이라면 플레이어 쪽을 바라보고있는것이다.
=> 두 벡터의 내적값이 양수일 때 플레이어쪽을 바라보고있는것이다.

Enemy1의 시선의 방향벡터가 파란 선이라면 플레이어와 수직의 방향을 바라보고있다.
=> 두 벡터의 내적값이 0일 때 플레이어와 수직의 방향을 보고있다.

Enemy1의 시선의 방향벡터가 초록 선이라면 플레이어 쪽을 바라보고있지 않은것이다.
=> 두 벡터의 내적값이 음수일 때 플레이어쪽을 바라보고있지 않은것이다.

이렇게 벡터의 내적값을 사용해서 객체 다른 객체를 바라보는지, 아닌지를 구할 수 있게 됐다.


면접관 : 벡터의 내적을 게임에 어떻게 적용할 수 있나요?

이제 두려워 하지 마세요!


이번 공부는 아래의 블로그에서 많은 도움을 받았다. 감사합니다!
https://gpfatp.blogspot.com/2019/08/1.html#

 

작성일: 2024년 10월 16일

 

편입공부를 했던 지금은 이 글이 다르게 읽힌다.

'아카이브 > 개발,공부' 카테고리의 다른 글

1인개발 후기  (0) 2026.05.12
Transform과 Rigidbody에 대해  (0) 2026.05.12
군것질 후기  (0) 2026.05.12
유니티,깃 협업 환경 세팅 (팀원 공유용)  (0) 2026.05.12
객체지향 프로그래밍  (0) 2026.05.12

배경

회사를 그만두게 되었다. 이런 때야말로 혼자 개발하기 딱 좋은 때라는 생각이 들었다. 마침 이직용 포트폴리오에 회사 코드를 첨부하기도 애매했었고, 1인 개발은 더욱 필연 같았다.

회사에서 새로 개발할 게임에 대해 이야기를 할 때, 늘 나왔던 말은 "빠르게 작업해서 빠르게 출시하자"였다. 그런데 늘 그렇게 되지 않았고, 루즈해지는 기간도 꽤 길었었다.

그래서 1인 개발이지만 이번에야말로 진짜로 빠르게 작업해서 빠르게 출시해보자! 라는 마음가짐으로 기획을 시작했었다. 내가 처음 계획한 기간은 기획, R&D, 개발, 디자인 합해서 40일이었다.

계획 세우기

회사에서 일감을 Notion이나 Trello로 나누는 것이 편했었는데, 노션을 무료로 사용할 때에 제한들이 좀 있기도 하고 어차피 혼자 사용할 거라서 Obsidian을 사용했다.

이때쯤엔 이것까지 하면 되겠다 싶을 정도로 계획을 잡았다. 그리고 필요하다고 생각되는 일들을 정리해서 시각화했다.

개발 들어가기 전

체계적으로 개발을 하고 싶었고 처음 3일 정도는 기획을 하면서 일종의 R&D를 병행했다. 이번 프로젝트에서 가장 욕심냈던 부분은 "객체지향적인 프로그래밍"이었다. 클래스 구조를 잘 짜보고 싶었고 그러려면 시작이 가장 중요할 것이라고 생각했다. 그래서 애초에 개발 전에 디자인 패턴이나 SOLID 원칙 등 가장 기본적인 부분부터 다시 복습도 하고 몰랐던 부분도 공부했다.

개발 시작


회사 일이 아니고, 혼자서만 일을 하려다 보니 게을러지는 것이 가장 큰 걸림돌일 것이라 생각했다. 그런데 일을 하나하나 해치워가면서 하니까 너무 재미있었고 그래서 더 열심히 했다. 위에 보면 포도알이 참 예쁘게 찍혔다.

게임 안의 코드들은 내가 회사에서 배운 것, 사용하던 것들을 거의 그대로 사용했다. 여긴 포트폴리오가 아니니까 모두 다 세세히 적기보다는 재밌었던 작업, 힘들었던 작업 몇 가지 적어보겠다.


재미있었던 것

1.어드레서블

회사에서도 어드레서블을 사용하기는 했었다. CTO님이 세팅을 하시고, 나는 빌드할 때 조심조심 업데이트만 하는 정도였다. 잘못 건드리면 큰일 난다! 느낌이 강해서 "제가 해봐도 될까요?"라고 섣불리 말씀을 못 드렸었다.

프로젝트의 에셋 다운로드를 local이 아닌 remote로 설정해서 인터넷에서 다운받도록 하는 기능이 있는데, 이 기능을 사용하면 스토어에 등록되는 앱의 용량도 줄일 수 있고, 에셋을 게임 내에서 추가로 받는다던가 스토어에서의 패치 없이 CDN 스토리지를 업데이트하는 것만으로도 게임의 에셋 일부를 업데이트할 수도 있다.

그리고 저번 프로젝트에서 이 기능을 쓰시려다 잘 안 돼서 결국 local로 돌리셨다는 사실을 나는 알고 있었다. 너무 remote가 쓰고 싶었고 열심히 조사했다.

CDN 서비스는 아마존이 아닌 Cloudflare에서 R2라는 이름으로 무료로 제공하고 있었다. 돈 아꼈다는 생각에 너무 신났었는데, 이걸 사용하려면 내 도메인이 필요했었다. 내 세상이 무너졌다.

그런데 카페24에서 도메인을 550원에 파는 이벤트를 하고 있었다. 비록 1년이라는 기간 제한이 있었지만 그때 돼서 연장하면 될 일이었다. 몹시 신났었다.

DNS, 네임서버 등 뭔가 낯선 단어들이 많았지만 하나하나 세팅하면서 개발을 같이 했고, 결국 성공했다!

2.인터페이스

별건 아니지만 인터페이스를 진짜 찐하게 써봤다. 회사 일할 때는 만들어져 있는 부모 클래스나 인터페이스를 분석해서 내가 쓰는 정도였는데, 시작부터 내가 하려니까 더 재미있었다.

지금까지는 객체끼리 충돌 처리를 할 때 CompareTag를 사용하고 각각 다른 getComponent를 쓰는 정도였다. 보통 다들 그렇게 하는 것 같다. 그런데 그러면 태그마다 다른 케이스로 코드를 작성해야 하니까 OnTrigger 함수 안의 코드가 굉장히 길어지고 지저분했었다.

그래서 이번에는 충돌하는 오브젝트들에게 IInteractable이라는 인터페이스를 붙여주었다. 그러면 플레이어의 OnTrigger 함수는

	//예시 코드
    private void OnTriggerEnter2D(Collider2D collision)
    {
        IInteractable interactableObject = collision.GetComponent<IInteractable>();
        if (interactableObject != null)
        {
            interactableObject.EnterSingleInteraction();
        }
    }

이게 전부다. 우물 안 개구리로서 작성한 코드라서 이게 더 좋은 코드가 맞는지 확신은 없지만 어마어마한 깔끔함에 이미 자지진 상태였다.

3.아트 리소스 제작

그림은 생각보다 빠르게 나왔었다. 프로젝트 시작한 지 7일 차에 캐릭터, 배경들의 이미지는 나왔었다. 그런데 애니메이션이 문제였다.


내가 원했던 애니메이션 스타일은 쫙쫙 늘어나고 띠용띠용거리는 귀여운 애니메이션이었다. 최근에 오구와 비밀의 숲을 인상 깊게 보기도 했고, 원래 그런 걸 좋아한다. 이건 한 장 한 장 이미지를 그려서 빠르게 교체하는 고전적인 방식으로 만드는 거라고 알고 있다. (혹시 그게 아니라면 제발 알려주세요)

그래서 한 번 직접 그려보려던 중 감이 안 와서 일단 납작해졌다가 늘어나는 정도만 Idle 애니메이션 용으로 해봤는데


즉시 포기했다. 이건 내가 접근할 만한 영역은 아니라고 판단했고 애니메이터 포폴 만드는 거 아니잖냐고 스스로를 달래면서 Spine을 사용하는 것으로 선회했다. 뼈대 하나하나 만지면서 애니메이션을 만드는 건 나름 재미있었다.

힘들었던 것

1. 물리메소드

오브젝트가 지정된 위치에서 스폰이 돼야 하는데, 로컬 기준 (0,0,0)에서 스폰된 채 뭔가 버벅거리는 일이 계속 생겼었다. 내가 개발할 때 가장 힘든 부분은 “간헐적으로 나타나는” 버그인 것 같다. 이 버그도 10번에 3번 정도만 발생해서 도대체 뭐가 원인인지 알 수가 없었다. 이것 때문에 이틀 정도를 통으로 사용한 것 같다.

유니티 Discussion에 직접 질문했고 찐하게 해결했다.
https://velog.io/@jajup/Transform%EA%B3%BC-Rigidbody%EC%97%90-%EB%8C%80%ED%95%B4

2. 귀찮은 일

1인 개발이라는 건 귀찮은 일까지 모두 혼자서 해야 된다는 걸 알고는 있었지만 뒤늦게 체감했다. 이번에 깨달은 건데 회사에서 내가 계속 해오던 일은 "틀"을 만들어주는 것뿐이고 기획자분들이 항상 그 틀 안에 내용물을 채워줬었는데 이게 보통 힘든 일이 아니다.

퇴사 전: 대화 기능 만들어주기
1인 개발: 대화 기능 만들고, 대사 채우고, 연출 위치,타이밍 잡기

퇴사 전: 데이터를 가진 캐릭터를 만들어주기
1인 개발: 데이터를 가진 캐릭터를 만들고 수많은 캐릭터에 데이터 채워주고 밸런스 신경 쓰기

등등… 저 데이터 테이블 채우는 부분이 너무 끔찍해서 거기서 가장 하기 싫었다. 동료의 소중함을 한 번 더 깨달았다. 차라리 개발을 시켜달라.


개발 끝

40일이라는 기간을 잡았었고, 39일 차에 모든 게 끝났다.
라고 생각했었으나, 생각보다 구글의 검토는 까다로웠다.

메타데이터가 올바르지 않다, 광고가 올바르지 않은 연령대를 타겟팅하지 않는다 등등 메일도 꽤 주고받았다. 진상이었나 싶기도 했지만 재촉도 좀 했다. ㅠㅠ 그렇게 계획한 날짜에서 11일이 지난 후. 글 작성 날짜 기준 어제 프로덕션으로 게임이 잘 올라갔다.

 

너무 빠른 개발, 기간에 집중한 탓인지 만들어놓고 보니

게임의 스케일이나 기술이 "자 포트폴리오입니다!"라고 하기에는 너무 소박한 것 같다.

 

이제 친구한테 맥북 빌려서 애플에도 한 번 앱을 올려볼 계획이다. 회사에서 애플 빌드를 하시는 분이 계셔서 내가 만져볼 기회가 없었다. 애플도 검토 단계에서 리젝을 참 잘 준다고 들었던 것 같은데 미리 긴장해야겠다.

 

어차피 이거 조금 고쳐라 저거 조금 고쳐라 정도이지 않을까. 결과나 메일 기다리는 시간이 더 길게 뻔하다. 미뤄뒀던 여러 가지 공부도 병행해야겠다.

 


작성일: 2024년 10월 13일

'아카이브 > 개발,공부' 카테고리의 다른 글

벡터의 내적과 외적 -내적  (0) 2026.05.13
Transform과 Rigidbody에 대해  (0) 2026.05.12
군것질 후기  (0) 2026.05.12
유니티,깃 협업 환경 세팅 (팀원 공유용)  (0) 2026.05.12
객체지향 프로그래밍  (0) 2026.05.12

작업 중 물체(GameObject)의 위치가 제대로 초기화되지않는 현상이 발견돼서, 소규모로 테스트를 만들어서 실행해보았다.

public class TransformTest : MonoBehaviour
{
    public Rigidbody2D _rb2D;
    public bool isRgMove;

    private void OnEnable()
    {
        SetPosition(Vector2.down);
    }

    private void OnDisable()
    {
        SetPosition(Vector2.up * 5);
    }

    protected void SetPosition(Vector2 newPosition)
    {
        if (isRgMove)
        {
            //_rb2D.position = newPosition;
            _rb2D.MovePosition(newPosition)
        }
        else
        {
            transform.position = newPosition;
        }
    }
}

물체를 enable/Disable 시에 isRgMove가 뭐던간에 게임오브젝트의 위치가 바뀌는것이 내가 생각한 정상적인 결과였다.

isRgMove가 true인 경우에는 생각한 대로 작동했지만 리지드바디를 사용한 이동은 약간의 지연이 있었다. 지연이 항상 있는것도 아니고, 10번에 3번정도 지연이 있었다. 그래도 그 작은 지연때문에 큰 버그가 생기는 중이었다.


RigidBody를 가진 물체의 이동

 transform.position = newPosition;

이 코드는 물체의 Position 을 직접 바꿔주는 코드다.
"이동시킨다" 보다는 순간이동에 가까운 코드다. 물리엔진을 우회하는 행위다.

순간이동 시에 게임오브젝트의 "위치"만 순간이동하기 때문에 Rigidbody나 Collider같은 물리적인 요소들은 같이 이동되지않고,뒤늦게 따라오게 되면서 리지드바디의 위치를 다시 계산해야한다.
(위의 컴포넌트가 없는 물체는 상관없다.)

_rb2D.position = newPosition

이 코드도 Transform이 아닌 리지드바디를조작하기는 하지만, 결국 포지션을 덮어씌움으로 물리엔진을 우회하고있다. transform.position보다는 빠르지만 안정적인 코드는 아니라고 알고있다.

그래서 RigidBody를 가진 물체를 이동시킬때에는 위의 두 코드보다,
아래의 코드가 빠르고 물리적으로 안정적이다.


Rigidbody.MovePosition

 _rb2D.MovePosition(newPosition)

이 친구는 Position을 직접 바꿔주는 코드처럼 물체를 순간이동 시키는 것이 아니라,
지정된 위치로 충돌,물리적 상호작용등을 고려해서 "이동"을 하는것이다.

굉장히 빠르고 안정적이라고 알고있다. 하지만 이것도 지연은 똑같이 있었다..


해결

(위의 테스트 말고) 실제 게임 내에서 문제가 됐던부분은
[물체 스폰 -> 순간이동] 의 흐름이 원활하지 않았던 것이다.

물리를 가진 물체를 순간이동 시키면 물리를 우회하고 안정적이지 않은 코드라고 앞서 말했었다.
그런데 게임오브젝트를 껐을때에는 물리도 같이 꺼진다.

그래서 스폰되기 전에 게임오브젝트가 꺼진 상태에서 transform.position을 사용해서 위치를 강제로 옮긴 후 게임오브젝트를 켰다.
[순간이동 -> 물체스폰] 의 순서로 바뀐것이다. 이것은 물리요소가 뒤따라오지도, 다시 계산할 필요도 없다고 한다.

_rb2D.MovePosition 을 사용해서 해결한 것은 아니지만 좋은 함수인것은 분명하다.


https://discussions.unity.com/t/there-is-a-delay-when-changing-rigidbody2d-position/1516558/2

한밤중에 궁금해서 이곳저곳 찾아보던 중 UnityDiscussion 커뮤니티에 가입해서 직접 질문해봤는데,
유니티 2D물리 담당 개발자분까지 나서서 답변을 성심성의껏 해주셨다.

해결방법은 너무 간단했지만, 과정중에 2D물리 이동에 대해서는 정말 찐하게 찾아보고 공부해본것같다.

감사합니다 Kurt-Dekker , MelvMay 선생님들...

Kurt-Dekker
https://discussions.unity.com/u/kurt-dekker/summary

MelvMay
https://discussions.unity.com/u/melvmay/summary

 

 


작성일: 2024년 9월 9일

'아카이브 > 개발,공부' 카테고리의 다른 글

벡터의 내적과 외적 -내적  (0) 2026.05.13
1인개발 후기  (0) 2026.05.12
군것질 후기  (0) 2026.05.12
유니티,깃 협업 환경 세팅 (팀원 공유용)  (0) 2026.05.12
객체지향 프로그래밍  (0) 2026.05.12

컴퓨터에 연결해서 쓸 유선 헤드폰을 샀다.

스피커랑 헤드폰이랑 왔다갔다하면서 쓰고싶었고, 헤드폰 선은 계속 꽂아둔 채로 출력기기를 쉽게 전환 하고싶었다. 따로 제어판을 들어가지않고 클릭이나 키입력 한번으로 말이다..

ms스토어에 있는 프로그램인데, 이거면 생각한대로 단축키를 딸깍 누르는 것만으로 출력 기기를 바꿀 수 있었다.

그런데 디스코드에서는 선을 직접 꽂고 빼고 하는게 아닌 이상 출력기기가 바뀌지 않았다.



추측하는 이유는 이렇다.
디스코드 오디오 설정에는 Default라는 항목이 있다. 이렇게 설정해두면 윈도우에서 설정된 기본 통신장치에서 통화소리가 나게 된다.


윈도우 소리 제어판에서 기본 장치  기본 통신장치 라는 두 단어를 볼 수 있었다.
위에 언급한 Audio Device Switcher는 기본장치를 바꿔주는 친구였다. 검색했을때 찾아본 프로그램들도 그랬다.
기본 통신장치는 그대로여서 노래는 스피커에서 나오고 디스코드 통화음성은 헤드폰으로 들리는 상황이라고 판단했다.


이런걸 설정하는 간단한 프로그램은 만들 수 있을것같았다.
유니티가 아니라 컴퓨터 시스템에 접근하는건 처음이어서 기대도 됐다.

작업했던 과정을 하나하나 캡쳐해두질 않아서,, 내 생각의 흐름이라도 잊어먹기 전에 적어서 남겨둘려고한다. 만드는 과정이 되게 재미있기도 했다.

처음 생각한 기능

위에 맨처음에 적었던 말 중에 "선은 계속 꽂아둔 채로" 가 있는데, 선을 아예 뽑고 꽂을때에는 기본통신장치까지 같이 잘 전환이 됐다.


이 [사용안함]을 누르게되면 선을 뽑은것과 같은 취급이 되는것 같다. 디스코드와 노래가 출력되는 기기가 잘 바뀌었다.
그래서 선이 꽂혀있기는 하지만 프로그램을 실행하면 헤드폰을 [사용안함] 으로 설정하도록 만들 수 있을것같았다.

1차 시기

  1. 비주얼스튜디오 c# 콘솔앱 프로그램으로 프로젝트를 만들었다. 이유는 평소에 콘솔앱으로 실용적인것을 만들어보고싶기도 했고, 사실 익숙한게 비주얼스튜디오 c#뿐이라서 선택했다..ㅎ
  2. 연결된 기기에 접근하려면 nuGet이라는 패키지가 필요했고, 설치 후 이것저것 구경했다.
  3. 받은 패키지의 메소드로 연결된 기기들의 목록을 쿼리로 받을 수 있었고, 기기마다 ID, Name, State 등의 여러 프로퍼티가 있었다.
  4. 그 중 State는 사용안함,사용중,연결끊김 등의 상태들이 저장돼있었다.
  5. 하지만 어떤 수를 써도 그 State를 코드로 직접 바꿔줄 수는 없었다.. 애초에 Get밖에 안되는 친구였고 set할수있는 함수도 없어보였다.

2차 시기

  1. [사용안함] 처리하는게 아니라, 상황마다 적절한 장치를 기본장치와 기본통신장치로 동시에 설정해주면 어떨까 싶었다. 스피커를사용중이면 헤드폰을 설정하고,헤드폰을사용중이면 스피커를 설정하도록.
  2. 설정하려고 쿼리문을 많이 보고쓰게 됐다. 덕분에 WMI 테스터라는것도 알게됐다.
  3. 결국 기본장치설정도 비주얼스튜디오에서 직접 바꿔줄 수 없었다. 내 능력 부족일수도 있지만.. 내가 찾아보기에는 방법이 없어보였다.
  4. 그러다 PowerShell을 사용하면 설정해줄 수 있다는걸 알게됐다!!

3차 시기

  1. PowerShell을 관리자권한으로 실행 후, Get-AudioDevice -List 를 하면 현재 연결된 오디오 장치의 리스트를 얻어올 수 있었다.
    여기서 저 Default와 DefaultCommunication이 기본장치와 기본통신장치를 뜻한다는걸 눈치로 알 수 있었다.
  2. 다음은 디바이스를 Name으로 구분해서 상황에따라 다른 기기를 기본장치와 기본통신장치로 설정하도록 해주었다.
$defaultDevice = Get-AudioDevice -List | where Default -eq $true

if ($defaultDevice.Name -eq "스피커(4- High Definition Audio Device)") {
    Get-AudioDevice -List | where Type -like "Playback" | where name -like "헤드폰(4- High Definition Audio Device)" | Set-AudioDevice -Verbose
} elseif ($defaultDevice.Name -eq "헤드폰(4- High Definition Audio Device)") {
    Get-AudioDevice -List | where Type -like "Playback" | where name -like "스피커(4- High Definition Audio Device)" | Set-AudioDevice -Verbose
} else {
    Write-Host "기본 장치를 확인할 수 없습니다."
}
  1. 너무 잘 작동했다. 일단 여기서 댄스 한사바리 제꼈다..
  2. 메모장에 .cmd파일을 작성했다. 파워쉘을 실행하고 위의 코드를 실행시키도록 만들었다. 순조로웠고, 실행파일을 누르자 컴퓨터가 나한테 뱉은 답은 아래와 같았다.
  3. 湲곕낯 ?μ튂瑜??뺤씤?????놁뒿?덈떎.
    계속하려면 아무 키나 누르십시오 . . .
  1. 잠시 좌절했지만 내가 코드에 적었던 한글이 문제가 됐을거라 생각했다. 장치의 Name으로 식별했었기 때문에 헤드폰(4- High Definition Audio Device) 같은 한글이름이 들어가서 그랬던건데, 장치의 index로(숫자로) 식별하도록 더 간단하게 바꾸었고, 오류시 뱉을 텍스트도 영어로 바꾸었다.
  1. 실행파일이 완성됐고 잘 작동하는것을 확인했다.

물론 디스코드까지 쉽게 전환되는 프로그램이 구글에 이미 있는데 내가 못찾은것 일수도 있다. 한 10분 찾아보면 있을거같기도 하다..

게임프로그래밍이 아니라 윈도우에 접근하는게 너무 어색했고 용어나 툴부터 여러가지 어려움이 있었다.
그래도 매일먹던밥 먹다가 군것질 한번 하니까 오히려 내가 하는 일에 더 흥미가 붙은 기분이다.

진짜 재미있었다.

 
 

 


작성일: 2024년 8월 20일

'아카이브 > 개발,공부' 카테고리의 다른 글

1인개발 후기  (0) 2026.05.12
Transform과 Rigidbody에 대해  (0) 2026.05.12
유니티,깃 협업 환경 세팅 (팀원 공유용)  (0) 2026.05.12
객체지향 프로그래밍  (0) 2026.05.12
Generic  (0) 2026.05.12


회사에서 개발중인 C 프로젝트를 플레이스토어에 내부테스트 버전으로 올렸는데,
"앱 업데이트를 출시하려면 2024년 8월 31일까지 대상 API 수준을 업데이트하세요."
라는 메세지가 있었다.

보기엔 별 일 아니었다. 간단하게 ProjectSettings에서 target API를 33->34로 바꿔주면 될 줄 알았다.

그렇게 빌드를 했을 때에 아래같은 에러가 뜨며 빌드가 중단됐다.

WARNING:We recommend using a newer Android Gradle plugin to use compileSdk = 34

This Android Gradle plugin (7.4.2) was tested up to compileSdk = 33

This warning can be suppressed by adding
    android.suppressUnsupportedCompileSdk=34
to this project's gradle.properties

The build will continue, but you are strongly encouraged to update your project to
use a newer Android Gradle Plugin that has been tested with compileSdk = 34

지금 사용중인 Android gradle plugin 버전은 targetAPI를 33까지만 지원한다는 내용이었다.


해결 과정

구구절절 보기

C 프로젝트 에서 사용중인 유니티 버전은 최신 lts버전인 2022.3.39f1였다.
해당 버전에서는 Android gradle plugin 7.4.2를 사용중이고, 이것은 API레벨34에 적합하지 않다는 로그가 나오고 있다.

너무 모순이었다. 한마디로
최신버전이 API34레벨을 지원하지 않는데, 구글에서는 API레벨34를 원한다는거였다.

/

분명 나랑 같은 처지의 개발자가 많을텐데, 구글링 했을때 그런 사람이 너무 적었다.
뭔가 이상하다는걸 눈치 챘어야했는데.. 한발 빼서 멀리서 보지 않고 또 머리 들이밀면서 깊숙히 파고들었다.

정말 많은 포럼과 문서를 읽었고 직접 파일을 타이핑해서 수정해보고, 여러가지 새 버전의 gradle 파일도 받아서 바꿔치기 해보고, 환경변수도 건드리는 등등 참 많이 해봤다. 회사 cto님이랑 구글밋까지 하면서 찾아보았다.

/

빌드는 계속 실패했고, 머리를 식히고 생각해보니 바로 어제 구글에 업데이트했던 N 프로젝트가 생각났다.
생각해보니 N 프로젝트에도 분명 API 34로 올리라는 메세지가 왔어서, 별 문제없이 내가 올렸었다. 근데 왜 C프로젝트만 이렇게 복잡할까?

당장 N 을켜서 C 와 뭐가 다른지 비교했다.

  1. N의 gradle 버전을 봤는데 C와 같은 7.4.2였다... ..... 😥
  2. N의 Minimum API Level은 24였고, C는 22였다.

그걸 말씀드리자 cto님이 그럼 일단 gradle 문제는 아니라고 말씀하셨고, 나도 그런것같았다..

그리고 N에서는 사용하지 않고 C에서 사용하는게 있었는데, 페이스북 sdk였다.

그리고 화면공유로 보여주신 화면에 minimum API Level을 올리면 된다는 글을 보았다.(링크는 찾으면 올리겠다.)


해결


Minimum API Level가 기존에는 22로 설정돼있었는데, 24로 바꾸어서 빌드하니 성공했다.

한것에 비해 해답은 허무했지만, 너무 다행이다.

 

 


작성일: 2024년 8월 1일



for (int i = 0; i < toggles.Length; i++)
{
    toggles[i].btn.onClick.AddListener(() => OnToggle(i));
}

처음 내가 작성했던 코드이다. 코드는 단순해서 따로 설명은 안하겠다..

내가 생각한 대로라면, toggles[0]을 클릭시 OnToggle(0)이 실행되고,
toggles[1]을 클릭시 OnToggle(1)이 실행됐어야 했다.

근데 뜬금없이 button들에서 toggles[5]가 실행되고있었다.


원인

()=> OnToggle(i)

위에서 람다식 안에 들어간 i는 for문에서 사용되는 외부 변수이다.

람다식이 실제 실행되기 이전에는 변수를 참조 형태로 가지고 있다.

버튼들에 들어간 함수는 OnToggle(0), OnToggle(1) 이 아니라
OnToggle(i) 이다. 값이 들어간것이 아니라 i 자체가 참조된것이다.

i는 반복문의 첫번째 사이클에서는 0이었지만,다음에는 1,2,3,4.. 이렇게 자꾸 바뀌는 외부 변수다.

toggles.Length는 5이고, 반복문에 의해 i는 0에서5까지 증가하지만
i < toggles.Length 라는 조건때문에 마지막에는 반복문 안의 함수를 실행하지는 못한 채 탈출한다. (0실행->1실행->2실행->3실행->4실행->5탈출)

이 시점의 i는 5이고, 결국 모든 버튼들에는 OnToggle(5) 가 들어갔던 것이다.


해결

단순하게 반복문 안에 변수를 하나 더 받아서 넣으면 해결이다.

 for (int i = 0; i < toggles.Length; i++)
 {
     var index = i;
     toggles[i].btn.onClick.AddListener(() => OnToggle(index));
 }

꽤 유명한 문제였다. Closure problem 라고 한다고 한다.
https://stackoverflow.com/questions/271440/captured-variable-in-a-loop-in-c-sharp

Invalid username or password.
fatal: Authentication failed for ~

소스트리에서 푸쉬를 시도하면 이런 오류가 뜨는 경우가 있다.
별 일 아니고 깃에서 사용중인 토큰의 기간이 만료된것이다. 새로 생성해주면 된다.

1.깃허브 - Settings 클릭

2.좌측 메뉴에서 DeveloperSettings 클릭

3.Tokens(Classic) 클릭

4.토큰 새로생성


5.Note에 토큰의 이름을 설정해주고, SelectScopes에서 repo만 선택 후 맨아래 Generate Token 클릭


6.토큰생성 완료. 혹시 모르니 따로 저장해두자.


7.소스트리 프로그램 종료시킨 후, 패스워드 파일 삭제
파일경로는 보통 C:\Users\USER\AppData\Local\Atlassian\SourceTree\passwd 이다.
(AppData가 안보인다면 숨김파일 보이기)

8.소스트리 실행. 다시 푸쉬 시도시 아래같은 창 뜸. PW에 6에서 받은 토큰 붙여넣기.

9.끝


얼마나 오랫동안 놀았으면 그새 토큰이 만료 됐을까... 진짜 반성하자 ㅠㅠ

 


작성일: 2023년 7월 19일

d5e8d741-a226-4eba-8b1f-e9d036ef90b7_프로젝트_받는법.pdf
2.10MB

 

 

작성일: 2023년 1월 7일

'아카이브 > 개발,공부' 카테고리의 다른 글

Transform과 Rigidbody에 대해  (0) 2026.05.12
군것질 후기  (0) 2026.05.12
객체지향 프로그래밍  (0) 2026.05.12
Generic  (0) 2026.05.12
값 타입, 참조 타입 / Boxing, UnBoxing  (0) 2026.05.12

+ Recent posts