프로덕트

아티클

전체 보기
송민우

송민우

“자연에 직선은 없습니다. 그리고 개발에도 직선은 없습니다.”


“자연에 직선은 없습니다. 그리고 개발에도 직선은 없습니다.”

저는 자연에 직선이 없다는 말을 자연의 기하학적 아름다움을 나타내는 말로만 생각해왔습니다.

f15d4a4b-6b32-453a-88a7-0eb5f6ca1707.webp

하지만 현재 회사에 있는 좋은 팀장님을 만나게 되면서 그 의미를 소프트웨어 엔지니어라는 직업에 확장해서 생각해보게 됐습니다.

저는 개발자라면 무조건 완성도 있고 무결한 시스템을 만들 수 있어야 한다는 강박감을 느껴왔습니다. 첫 직장을 스타트업으로 취직하면서 도전하는 업무들을 많이 접하다보니 잘해내야만 한다는 마음가짐이 자리잡아온 것이 이유인 것 같습니다. 이런 강박감은 내가 쓴 코드를 신뢰하지 못하게 만들었고 나를 불안하게 만드는 요인이 됐습니다.

그러나 어떤 시스템이든 개선해나가고 더 튼튼히 세워나가는 것이 중요하다는 것을 팀장님을 통해 알게 되었습니다. 그리고 내가 존경하는 개발자들의 코드 또한 처음부터 직선이 아니라 곡선이 되었던 작은 점들을 다시 모아 직선으로 만들어가는 과정을 겪었을 것이란 생각이 들었습니다.

좋은 동료들과 함께 작은 점들을 모아 직선을 만들어가는. 좋은 엔지니어가 될 수 있기를 바라며 오늘 하루를 회고합니다.

4
0
송민우

송민우

[감정저장소] Flutter-Flame으로 물리엔진 구현하기

bac.png

감정저장소의 개발과 디자인을 둘 다 진행하면서 느꼈던 회고를 작성합니다.

#Flame이 뭐야?

Flutter가 3.10버젼을 릴리즈하면서 SKIA엔진을 Impeller엔진으로 대체했고, 덕분에 UI상의 버벅거리는 현상인 Jank가 많이 줄어들게 됐습니다. Flutter를 1.0 버젼에서부터 사용했던 사람으로서 체감을 많이 할 수 있었고 Flutter가 성능개선을 계속해서 지원해주는 것에 항상 감사를 느끼고 있습니다 😊

Flutter팀은 엔진교체를 예고함과 동시에 3.0 버젼을 릴리즈하면서 Flutter Casual Game ToolKit 을 소개했습니다. Flutter Casual Game ToolkKit은 규모가 작은 게임을 Flutter로도 개발할 수 있다는 것을 확인시켜줬습니다. 이 ToolKit에 사용된 핵심 라이브러리 바로 Flame 입니다! 😃

Flutter Casual Games Toolkit

#감정저장소에는 어떻게?

메이커로그_캡쳐_3.gif

위 GIF를 보면 감정조약돌들이 위에서 아래로 떨어지고, 서로 부딪치는 것을 확인할 수 있습니다. 우리가 흔히 말하는 물리엔진이 오브젝트들에 적용되어 있기 때문이에요!

Flame 라이브러리에는 다양한 기능을 제공하는 서드파티 라이브러리들이 포함되어있는데, 그 중에서도 물리엔진 기능을 제공하는 Forge2DGame라이브러리가 사용되고 있습니다.

아래 코드를 보면서 자세히 설명해보겠습니다 🙂

final screenSize = Vector2(1500, 1700); // 해상도

class PhysicScreen extends Forge2DGame {
  PhysicScreen()
      : super(
          zoom: 100,
          gravity: Vector2(0, 30),
          cameraComponent: CameraComponent.withFixedResolution(
            width: screenSize.x,
            height: screenSize.y,
          ),
        );

...

Forge2DGame 인터페이스를 상속받아 중력값인 gavity를 Vector2() 값으로 넣어주고, Flame게임의 스크린 크기를 결정하기 위해 CameraComponent 오브젝트에 선언되어있는 withFixedResolution Constructor를 사용해 해상도 값을 넣어줍니다.

...

@override
  Future<void> onLoad() async {
    camera.backdrop.add(_Background(size: screenSize));
  }
}|

class _Background extends PositionComponent {
  _Background({super.size});
  @override
  void render(Canvas canvas) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.x, size.y),
        Paint()..color = Colors.transparent);
  }
}

그리고 Forge2DGame 인터페이스는 onLoad, onAttach, onMount, onDispose 등의 기능을 제공해줍니다. 이 중에서 onLoad() 함수는 최초에 컴파일단계에서 게임속 세상의 오브젝트를 불러올 때, 실행되고 여기서 배경색상을 지정해줄 수 있습니다.

실제 떨어지는 공이 만들어지는 코드는 다음과 같아요.

스크린샷 2023-12-04 오후 3.35.29.png

class EmotionBall extends BodyComponent {
  final String assetURL;

  EmotionBall({required this.assetURL});
  double size = 1.6.w;

  @override
  Future<void> onLoad() async {
    await super.onLoad();
    final sprite = Sprite(game.images.fromCache(assetURL));
    add(
      SpriteComponent(
        sprite: sprite,
        size: Vector2(3.2.w, 3.2.w),
        anchor: Anchor.center,
      ),
    );
  }

...
  

오브젝트에 직접 색상을 입힐 수도 있고, 유니티를 사용해보신 분들이라면 잘 아실 Sprite를 오브젝트에 입혀줄 수 있습니다.

@override
  Body createBody() {
    final bodyDef = BodyDef(
      position: Vector2(worldSize.x / 2, -1),
      type: BodyType.dynamic,
    );
    final shape = CircleShape()..radius = size;
    final fixtureDef = FixtureDef(shape)
      ..density = 3
      ..friction = 1
      ..restitution = 0.4;
    return world.createBody(bodyDef)
      ..createFixture(fixtureDef)
      ..angularVelocity = radians(100);
  }
}

가장 핵심적인 기능은 위의 코드라고 할 수 있는데요. Forge2D라이브러리의 BodyComponent클래스에서 지원하는 createBody() 함수를 통해 최초 생성되는 위치인 position, 그리고 BodyType을 결정해줄 수 있습니다.

BodyType은

고정되어서 말그대로 고정적인 위치값을 갖게되는 static

zero velocity, zero mass, manually moved

static과 동일하지만, 게임 로직을 통해 움직임을 줄 수 있는 kinetic

zero velocity, zero mass, moved by solver

그리고 자유로운 물리효과를 받는 dynamic

with positive mass, non-zero velocity, determined by forces, moved by solver

을 지원합니다.

BodyType을 결정해주는 코드 아래에서는 mass의 속성들을 결정해줄 수 있습니다. 🙂

화면 기록 2023-12-04 오후 3.39.22.gif

가운데에 돌아가는 Box는 Kinetic 타입, 떨어지는 공들은 중력의 영향을 받는 Dynamic 타입의 오브젝트입니다

추가적으로 Forge2DGame 인터페이스는 microseconds 프레임을 지원하는 update 메소드를 제공하는데요.

기기사양에 따라서 지원되는 프레임수는 유동적이라고 하네요 😃

@override
  void update(double dt) async {
	...
}

#감정저장소의 다가오는 업데이트는?

[이미지]

감정저장소의 1.1 버젼에는 지금까지 내가 작성한 모먼트들을 기반으로 이 달의 데이터를 분석해주는 기능을 추가할 예정입니다 🙂 현재 열심히 개발중에 있구요, 혹시 오늘 메이커로그를 보고 관심이 생기신 분이 계시다면 감정저장소를 사용해주세요 :)

사용해보시고 피드백을 주시면 적극적으로 반영하고 싶습니다!

스크린샷 2023-12-04 오후 3.42.35.png

(다음에 이어서,...)

감정저장소

잊고 싶지 않은 감정을 저장해줄게

6
2
송민우

송민우

[감정저장소] 손해 없는 트레이드 오프

bac.png

감정저장소의 개발과 디자인을 둘 다 진행하면서 느꼈던 회고를 작성합니다.

# 필요이상으로 깎여나가는 피쳐의 살덩이

감정저장소에는 여러 애니메이션 효과들이 적용되어 있습니다. 특히 물리엔진을 적용한 애니메이션 효과는 제가 꼭 도전해보고 싶은 피쳐였기 때문에 디자인을 할 때부터 고려하고 시작했어요.

덕분에 사용자의 감정유형을 테스트하고 결과지를 받을 수 있는 프로세스를 완성할 수 있었습니다 😊

메이커로그_캡쳐_3.gif

디자인과 개발을 둘 다 할 때 성장시킬 수 있는 능력 중 한가지를 해당 피쳐를 개발하는 과정에서 느낄 수 있었는데요, 바로 손해 없는 트레이드 오프를 결정할 수 있는 능력입니다. 아래 예시와 함께 고민해보겠습니다.

보통 피쳐 단위로 스프린트 혹은 제품개발을 진행할 때, 개발자들은 구현가능성과 구현일정 검토를 확실하고 냉정하게 진행합니다.

하지만 전달받은 디자인들이 일반 제품 개발에서 자주 사용되는 기능이 아니거나 새로운 연구가 필요한 경우에는 보수적으로 판단하기 쉽다고 생각합니다. 📐

메이커로그_캡쳐_3.png

이 때 디자이너와 개발자 서로의 의견을 조율하면서, PM이 필요 이상으로 피쳐의 살덩이를 깎아내는 것을 자주 경험했습니다. 디자이너와 개발자가 둘 다 맞다고 생각하는 지점을 찾는 것은 각 직군의 언어와 배경이 다르다보니 정확도가 떨어질 수 밖에 없죠.

#개발과 디자인의 상황을 둘 다 명확히 이해할 수 있다면?! 🙂

저는 물리엔진 구현과 같이 문서의 언어로만으로는 풀어내기 어려운 작업일 수록 두 영역을 이해하는 능력의 효과가 크다고 느꼈습니다.

어떻게 개발을 진행할 것인지 대략적으로 알고 있기 때문에, 디자이너 송민우는 요구사항이 최대한 보존되는 선에서 UI를 만들고, 필요하다면 개발이 현실적으로 가능한 선에서 퀄리티를 높힐 수 있었습니다.

개발자 송민우의 입장에서도 왜 이렇게 UI를 만들었는지 이해하고 있기 때문에 놓치지 않아야할 디테일들을 잘 챙길 수 있고, 반대로 포기할 수 있는 사소한 UI 변경들을 실시간으로 판단할 수 있었죠. 👍

메이커로그 _4.jpeg

더 좋은 말로 표현할 수도 있을 것 같은데요,

저는 이런 경험을 혼자서 하면서 손해없는 트레이드 오프라는 말이 떠올랐습니다.

#그래서??

디자인과 개발의 기술적인 것을 이해하려하는 것 보다 각자 어떤 제품을 만들고 싶은지를 이해하고 얼라인을 맞추는 것이 먼저라고 생각됩니다. 저 혼자 작업하는데도, 디자이너 송민우가 완성시키고 싶은 내용과 개발자 송민우가 도전하고 싶은 내용이 다를 때가 많았거든요. 😅

각자 경험하시고 느끼시는게 또 다를 수도 있다는 생각이 들어 짧은 경험으로 조심스럽게 제가 느꼈던 인사이트를 글로 적어봤습니다. 다음 글에서는 물리엔진을 어떻게 구현했는지 적어보고 싶습니다.

유익하다고 느끼셨다면 감정저장소를 응원해주세요! :)

(다음에 이어서)

감정저장소

잊고 싶지 않은 감정을 저장해줄게

6
3
송민우

송민우

[감정저장소] 혼자서 기획 디자인 개발 다 해보기

Frame 91.png

#왜 혼자서 다 해봐?

저는 개발과 디자인을 둘 다 좋아하고, 잘하고 싶은 사람입니다.

개발과 디자인을 둘 다 할 수 있다는 건 제품 개발 기간을 단축시키고 기획 요구사항이 중간과정에서 수정될 여지를 줄일 수 있는 능력이라고 생각합니다.

하지만 두가지 영역의 실력을 지속적으로 갈고 닦는 것은 쉽지 않고, 조직에서 능력을 선뜻 내 보이기도 어렵다고 느꼈습니다.

그래서! 사이드 프로젝트를 시작했습니다.

앞으로도 디자인과 개발을 둘 다 잘하고 싶을뿐만 아니라 사이드 프로젝트를 진행하면서 디자이너로 쌓았던 경험을 잃지 않고, 디자인된 결과물을 그대로 개발로 만드는 실력을 키우고 싶었기 때문이죠.

스크린샷 2023-11-24 오전 10.02.25.png

노션과 피그마로 직접 제품기획과 디자인도 하고 😊

스크린샷 2023-11-24 오전 11.09.37.png

Flutter 프레임웍으로 개발도 했습니다 🙂

#어떤 내용을 공유하고 싶니

앱의 1.0 버젼은 개발을 완료해서 앱스토어에도 배포가 됐지만, 제품을 만들어가는 과정을 기록해둔 것이 없어서 지금이라도 메이커로그를 쌓아나갈려고 합니다.

앞으로 글을 작성해가면서,

디자이너 송민우가 개발자 송민우를 위해 어떻게 디자인을 했고

반대로는 개발자 송민우가 디자이너 송민우의 요구사항을 어떻게 구현했는지 나누고 싶습니다.

(다음편에 이어서…)

감정저장소

잊고 싶지 않은 감정을 저장해줄게

16
5

포스트

아직 포스트가 없습니다.