레이블이 Unity인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Unity인 게시물을 표시합니다. 모든 게시물 표시

2025년 8월 3일 일요일

작업 효율 200% 상승! Unity & Blender 필수 단축키 총정리 (표 형식)

안녕하세요, 개발자 및 아티스트 여러분! 3D 콘텐츠 제작의 양대 산맥, UnityBlender를 사용하면서 작업 속도 때문에 고민이신가요? 마우스 클릭만으로는 전문가의 속도를 따라가기 벅찹니다. 작업 효율을 극대화하고 싶다면, 단축키 사용은 선택이 아닌 필수입니다.

오늘은 여러분을 '단축키 마스터'로 만들어 드릴 Unity와 Blender의 핵심 단축키를 기능별로 보기 쉽게 표로 총정리했습니다. 이 포스트 하나만 저장해두시면 작업 효율이 200% 상승할 거예요!

Unity 필수 단축키

게임 개발의 흐름을 끊지 않고 빠르게 작업할 수 있도록 도와주는 Unity의 핵심 단축키입니다.

기본 및 파일 관리

기능 단축키 설명
씬 저장 Ctrl + S 현재 작업 중인 씬을 저장합니다. 가장 자주 사용합니다.
프로젝트 저장 Ctrl + Shift + S 프로젝트 전체의 변경 사항을 저장합니다.
새 씬 Ctrl + N 새로운 씬을 생성합니다.
씬 열기 Ctrl + O 기존에 저장된 씬을 엽니다.
실행/일시정지/스텝 Ctrl+P / Ctrl+Shift+P / Ctrl+Alt+P 게임을 실행, 일시정지, 한 프레임씩 실행합니다.

오브젝트 조작 및 씬 뷰

기능 단축키 설명
핸드 툴 (화면 이동) Q 또는 마우스 휠 버튼 클릭 씬 뷰를 상하좌우로 이동합니다.
이동 툴 W 선택한 오브젝트의 이동 기즈모를 활성화합니다.
회전 툴 E 선택한 오브젝트의 회전 기즈모를 활성화합니다.
스케일 툴 R 선택한 오브젝트의 크기 조절 기즈모를 활성화합니다.
렉트 툴 T 2D 스프라이트나 UI 요소의 크기 및 위치를 조절합니다.
선택 오브젝트 포커스 F 하이어라키에서 선택한 오브젝트를 씬 뷰의 중심으로 가져옵니다.
오브젝트 복제 Ctrl + D 선택한 오브젝트를 그 자리에 복제합니다.
버텍스 스냅 V 키를 누른 상태로 이동 오브젝트를 다른 오브젝트의 버텍스(정점)에 정확히 붙입니다.

Blender 필수 단축키

모델링부터 렌더링까지, Blender의 방대한 기능을 빠르게 사용하기 위한 필수 단축키입니다.

공통 및 오브젝트 모드

기능 단축키 설명
이동 / 회전 / 크기 G / R / S 선택한 오브젝트를 이동, 회전, 크기 조절합니다.
오브젝트 추가 Shift + A 메쉬, 라이트, 카메라 등 새로운 오브젝트를 추가합니다.
오브젝트 삭제 X 또는 Delete 선택한 오브젝트를 삭제합니다.
검색 F3 블렌더의 모든 기능을 이름으로 검색하여 실행합니다.
카메라 뷰 Numpad 0 현재 활성화된 카메라의 시점으로 전환합니다.
정면/측면/윗면 뷰 Numpad 1 / Numpad 3 / Numpad 7 오브젝트를 정면, 측면, 위에서 보는 시점으로 전환합니다.
오브젝트/에디트 모드 전환 Tab 선택한 오브젝트의 오브젝트 모드와 에디트 모드를 전환합니다.

에디트 모드 (모델링)

기능 단축키 설명
점/선/면 선택 모드 1 / 2 / 3 버텍스(점), 엣지(선), 페이스(면) 선택 모드로 전환합니다.
돌출 (Extrude) E 선택한 점/선/면을 돌출시켜 새로운 지오메트리를 생성합니다.
면 삽입 (Inset) I 선택한 면 안쪽에 새로운 면을 삽입합니다.
베벨 (Bevel) Ctrl + B 선택한 엣지나 버텍스를 부드럽게 깎습니다.
루프컷 (Loop Cut) Ctrl + R 오브젝트에 엣지 루프를 추가하여 디테일을 높입니다.
채우기 (Fill) F 선택한 버텍스나 엣지 사이를 면으로 채웁니다.

이 단축키들을 손에 익히는 것만으로도 여러분의 작업 속도는 눈에 띄게 향상될 것입니다. 지금 바로 Unity와 Blender를 켜고 하나씩 연습해보세요. 즐거운 창작 활동이 되기를 바랍니다!

2025년 8월 1일 금요일

[심층 분석] GoF 상태(State) 패턴: 실전 예제로 완벽 마스터하기 (JavaScript, Unity/C#)

안녕하세요, 개발자 여러분! 코드를 작성하다 보면 수많은 if-else 또는 switch 문 때문에 골머리를 앓았던 경험, 다들 한 번쯤 있으시죠? 객체의 '상태'에 따라 '행동'이 달라져야 할 때, 이 조건문들은 눈덩이처럼 불어나 코드를 복잡하게 만들고 유지보수를 악몽으로 만듭니다.

오늘은 이 문제를 아주 우아하고 객체지향적으로 해결해주는 강력한 도구, GoF(Gang of Four)의 상태(State) 패턴에 대해 기본 개념부터 실전 응용 예제까지 깊이 있게 파헤쳐 보겠습니다.

상태 패턴, 왜 필요할까요? (문제 인식)

상태 패턴의 진가를 알기 위해 먼저 문제가 되는 상황을 살펴보겠습니다. 온라인 문서 편집기를 만든다고 상상해봅시다. 문서는 '초안', '검토 중', '발행됨'이라는 세 가지 상태를 가집니다.

  • 초안 (Draft): 글 수정 가능, 발행 요청 시 '검토 중' 상태로 변경.
  • 검토 중 (Moderation): 글 수정 불가, 승인 시 '발행됨' 상태로 변경.
  • 발행됨 (Published): 글 수정 불가, 더 이상 상태 변경 없음.

가장 직관적인 방법은 클래스 내부에 상태를 나타내는 변수를 두고, 각 메서드에서 이 변수를 확인하여 분기 처리하는 것입니다.


// 안티 패턴: 상태에 따른 분기문이 늘어나는 경우
class Document {
  constructor() {
    this.state = 'Draft';
    this.content = '';
  }

  publish() {
    if (this.state === 'Draft') {
      console.log('문서 검토 요청!');
      this.state = 'Moderation';
    } else if (this.state === 'Moderation') {
      console.log('문서 발행!');
      this.state = 'Published';
    } else if (this.state === 'Published') {
      console.log('이미 발행된 문서입니다.');
    }
  }
  // write(), reject() 등 다른 메서드에도 비슷한 if-else 구조가 반복...
}
    

지금은 상태가 3개뿐이라 괜찮아 보입니다. 하지만 '보관됨(Archived)', '임시 삭제(Trashed)' 같은 새로운 상태가 추가된다면 어떨까요? 모든 메서드를 찾아다니며 else if 블록을 추가해야 합니다. 이는 OCP(개방-폐쇄 원칙)를 정면으로 위반하며, 코드는 점점 더 이해하기 어렵고 수정하기 힘든 '스파게티 코드'가 되어갑니다.


해결책: 상태 패턴의 구조

상태 패턴은 '객체의 내부 상태가 변할 때, 객체의 행동을 마치 클래스가 바뀐 것처럼' 만들어줍니다. 즉, 상태 자체를 객체로 만드는 것입니다.

핵심 아이디어: 상태와 관련된 로직을 별도의 '상태 객체'로 뽑아내고, 원래 객체(Context)는 현재 상태 객체에게 행동을 위임한다.

상태 패턴은 주로 세 가지 역할로 구성됩니다.

  • Context (문맥): 상태를 가지는 주체. (e.g., Document) 현재 상태를 나타내는 State 객체에 대한 참조를 가집니다. 클라이언트는 Context 객체와 상호작용합니다.
  • State (상태): 모든 구체적인 상태들이 따라야 할 공통 인터페이스. 상태에 따라 달라지는 행동 메서드들을 정의합니다. (e.g., publish(), write())
  • ConcreteState (구체적인 상태): State 인터페이스를 구현한 클래스. (e.g., DraftState, ModerationState) 각 상태에 맞는 실제 행동을 구현하고, 필요에 따라 Context의 상태를 다음 상태로 전환하는 책임을 가집니다.

실전 예제 1: 문서 편집기 리팩토링 (JavaScript)

위에서 본 문서 편집기 예제를 상태 패턴으로 리팩토링해 보겠습니다.

1. State 인터페이스와 ConcreteState 클래스 정의


// State 인터페이스 (추상 클래스 역할)
class State {
  publish(doc) { throw new Error('하위 클래스에서 구현해야 합니다.'); }
  write(doc, text) { throw new Error('하위 클래스에서 구현해야 합니다.'); }
}

// ConcreteState: 초안 상태
class DraftState extends State {
  publish(doc) {
    console.log('문서를 검토 상태로 전환합니다.');
    doc.changeState(new ModerationState());
  }
  write(doc, text) {
    doc.content += text;
    console.log('내용 추가: ' + text);
  }
}

// ConcreteState: 검토 중 상태
class ModerationState extends State {
  publish(doc) {
    console.log('문서를 최종 발행합니다.');
    doc.changeState(new PublishedState());
  }
  write(doc, text) {
    console.log('[경고] 검토 중인 문서는 수정할 수 없습니다.');
  }
}

// ConcreteState: 발행됨 상태
class PublishedState extends State {
  publish(doc) { console.log('[알림] 이미 발행된 문서입니다.'); }
  write(doc, text) { console.log('[경고] 발행된 문서는 수정할 수 없습니다.'); }
}
    

2. Context 클래스 정의


// Context 클래스
class Document {
  constructor() {
    this.state = new DraftState(); // 초기 상태 설정
    this.content = '';
  }

  // 상태를 변경하는 책임은 Context가 가짐
  changeState(newState) {
    this.state = newState;
  }

  // 모든 행동을 현재 상태 객체에 위임
  publish() {
    this.state.publish(this);
  }

  write(text) {
    this.state.write(this, text);
  }
}
    

이제 Document 클래스는 자신의 상태가 무엇인지 신경 쓰지 않습니다. 그저 현재 state 객체에게 "이거 해줘"라고 명령만 내리면 됩니다. 상태 추가가 필요하면? 새로운 State 클래스를 만들기만 하면 끝입니다. 기존 코드는 전혀 건드릴 필요가 없죠!


실전 예제 2: 게임 개발과 상태 패턴 (Unity/C#)

상태 패턴이 가장 빛을 발하는 분야 중 하나는 바로 게임 개발입니다. 플레이어 캐릭터는 '서있기', '걷기', '달리기', '점프하기', '공격하기' 등 수많은 상태를 가집니다. 각 상태에서 가능한 입력과 행동은 완전히 다릅니다. (예: 점프 중에는 또 점프할 수 없음)

Unity(C#) 환경에서 플레이어 캐릭터의 상태를 관리하는 코드를 상태 패턴으로 구현해 보겠습니다.

1. State 인터페이스와 Context(Player) 정의


// C#에서는 interface를 사용하여 State의 규약을 정의합니다.
public interface IPlayerState
{
    // 모든 상태는 진입(Enter), 실행(Execute), 탈출(Exit) 로직을 가질 수 있습니다.
    void Enter(PlayerController player);
    void Execute(PlayerController player);
    void Exit(PlayerController player);
}

// Context 역할을 하는 PlayerController (MonoBehaviour를 상속)
public class PlayerController : MonoBehaviour
{
    private IPlayerState _currentState;

    public void Start()
    {
        // 게임 시작 시 초기 상태(Idle)로 설정
        ChangeState(new IdleState());
    }

    public void Update()
    {
        // 매 프레임마다 현재 상태의 Execute 로직을 실행
        if (_currentState != null)
        {
            _currentState.Execute(this);
        }
    }

    public void ChangeState(IPlayerState newState)
    {
        // 기존 상태의 Exit 로직 실행
        if (_currentState != null)
        {
            _currentState.Exit(this);
        }
        
        // 새로운 상태로 변경하고 Enter 로직 실행
        _currentState = newState;
        _currentState.Enter(this);
    }
}
    

2. ConcreteState 클래스들 정의


// ConcreteState: 서있기 상태
public class IdleState : IPlayerState
{
    public void Enter(PlayerController player) { Debug.Log("상태: 서있기"); }
    public void Exit(PlayerController player) {}
    public void Execute(PlayerController player)
    {
        // 이동 키가 입력되면 WalkState로 변경
        if (Input.GetAxisRaw("Horizontal") != 0)
        {
            player.ChangeState(new WalkState());
        }
        // 점프 키가 입력되면 JumpState로 변경
        else if (Input.GetKeyDown(KeyCode.Space))
        {
            player.ChangeState(new JumpState());
        }
    }
}

// ConcreteState: 걷기 상태
public class WalkState : IPlayerState
{
    public void Enter(PlayerController player) { Debug.Log("상태: 걷기"); }
    public void Exit(PlayerController player) {}
    public void Execute(PlayerController player)
    {
        // 이동 로직 처리...

        // 이동 키 입력이 없으면 IdleState로 변경
        if (Input.GetAxisRaw("Horizontal") == 0)
        {
            player.ChangeState(new IdleState());
        }
    }
}

// ConcreteState: 점프 상태
public class JumpState : IPlayerState
{
    public void Enter(PlayerController player) 
    { 
        Debug.Log("상태: 점프!");
        // 실제 점프 물리 로직 실행
    }
    public void Exit(PlayerController player) {}
    public void Execute(PlayerController player)
    {
        // 땅에 착지했는지 확인 후 IdleState로 변경
        if (player.isGrounded) // isGrounded는 플레이어가 직접 구현해야 함
        {
            player.ChangeState(new IdleState());
        }
    }
}
    

이 구조를 사용하면 PlayerControllerUpdate 메서드는 매우 깔끔하게 유지됩니다. 각 상태에 대한 로직과 다른 상태로의 전환 조건은 해당 상태 클래스 내에 완벽하게 캡슐화됩니다. '공격하기', '방어하기', '스킬 사용' 등 새로운 상태를 추가하는 것은 그저 IPlayerState를 구현하는 새 클래스를 만드는 것만으로 충분합니다.


상태 패턴, 언제 사용해야 할까요?

  • 객체의 행동이 내부 상태에 따라 극적으로 변할 때
  • 코드에 상태를 확인하는 조건문(if/else, switch)이 너무 많고 복잡할 때
  • 상태와 관련된 로직을 한 곳에 모아 응집도를 높이고 싶을 때
  • 새로운 상태를 추가할 때 기존 코드를 수정하고 싶지 않을 때 (OCP 준수)

결론: 복잡성과 작별하는 방법

상태 패턴은 초기에 여러 클래스를 만들어야 해서 다소 번거롭게 느껴질 수 있습니다. 하지만 객체의 상태가 2~3개를 넘어가고 상태별 행동이 복잡해지는 순간, 이 패턴은 엄청난 유지보수성과 확장성을 선물해 줍니다.

복잡한 조건 분기문 때문에 코드가 엉망이 되어가고 있다면, 더 이상 망설이지 마세요. 상태 패턴을 도입하여 각 상태에 '역할'과 '책임'을 부여하고, 여러분의 코드를 한 단계 더 성숙시켜 보시길 바랍니다.

작업 효율 200% 상승! Unity & Blender 필수 단축키 총정리 (표 형식)

안녕하세요, 개발자 및 아티스트 여러분! 3D 콘텐츠 제작의 양대 산맥, Unity 와 Blender 를 사용하면서 작업 속도 때문에 고민이신가요? 마우스 클릭만으로는 전문가의 속도를 따라가기 벅찹니다. 작업 효율을 극대화하고 싶다...