본문 바로가기

블루프린트에서의 상속 기본 개념

부모와 자식 블루프린트가 기능을 물려받는 원리를 이해하고 공통 로직 재사용과 오버라이드 방법을 익힙니다.

나무 문, 철문, 자동문을 각각 따로 만들면 처음에는 빠르지만, "문이 열릴 때 소리를 낸다"는 공통 규칙 하나를 바꾸려면 세 블루프린트를 모두 열어 같은 수정을 반복해야 합니다. 하나라도 빠뜨리면 문마다 동작이 달라집니다. 객체 지향 프로그래밍(Object-Oriented Programming, OOP)의 상속(inheritance)은 이런 공통 부분을 한곳에 두고, 종류마다 다른 부분만 따로 만들 수 있게 해 줍니다.

이 글에서는 블루프린트에서 상속이 무엇을 물려주는지, 부모 클래스를 골라 자식 블루프린트를 만드는 방법, 그리고 물려받은 함수를 자식에서 다시 정의하는 오버라이드(override)와 부모 구현을 함께 실행하는 부모 호출을 살펴봅니다.


상속은 무엇을 물려주는가

상속은 기존 클래스의 속성(변수)과 기능(함수, 이벤트)을 새 클래스가 물려받는 것입니다. 물려주는 쪽을 부모 클래스(parent class, base class), 물려받는 쪽을 자식 클래스(child class, derived class)라고 부릅니다. 자동차라는 부모 클래스에 "움직인다", "탈 수 있다" 같은 공통 기능이 있다면, 스포츠카와 트럭을 자식 클래스로 만들 때 공통 기능은 다시 만들지 않고 물려받은 뒤, 빠른 가속이나 짐 싣기처럼 각자의 특징만 더하면 됩니다.

블루프린트에서도 어떤 액터 블루프린트를 다른 액터 블루프린트의 자식으로 만들면, 부모의 컴포넌트 구성과 변수, 함수, 이벤트를 물려받습니다. 다만 물려받았다고 모두 마음대로 바꿀 수 있는 것은 아닙니다. 함수의 접근 지정(Public, Protected, Private)과 변수의 Private 설정, 편집 허용 여부에 따라 자식에서 직접 읽거나 바꿀 수 있는 범위가 달라집니다. 예를 들어 Private으로 지정한 함수는 그 함수를 만든 블루프린트 안에서만 호출할 수 있습니다.

상속은 호환되는 클래스 계층 안에서만 이어집니다. 액터 블루프린트와 컴포넌트 블루프린트는 둘 다 클래스를 정의하지만 서로 다른 계층에 속하므로, 액터를 컴포넌트의 자식으로 만드는 식으로 두 계층을 섞을 수는 없습니다.


상속이 주는 세 가지 이점

첫째는 재사용입니다. 이미 만든 기능을 다시 만들 필요가 없으므로 개발 시간이 줄고, 같은 로직을 여러 번 옮겨 적다가 생기는 실수도 줄어듭니다.

둘째는 유지보수입니다. 부모에서 물려준 구현을 고치면, 그 구현을 그대로 쓰는 모든 자식에 수정이 반영됩니다. 다만 자식이 그 함수를 오버라이드했거나 기본값을 따로 덮어썼다면 그 자식은 부모의 변경을 그대로 받지 않으므로, 부모를 고친 뒤에는 그런 자식이 있는지 함께 확인해야 합니다.

셋째는 확장입니다. 부모의 기존 기능을 건드리지 않고 자식에 새 기능을 더하거나, 물려받은 기능을 자식에서 오버라이드해 특정 동작만 바꿀 수 있습니다.


부모 클래스를 골라 자식 만들기

새 블루프린트 클래스를 만들 때 가장 먼저 나타나는 창이 부모 클래스 선택(Pick Parent Class) 창입니다. 여기서 고른 클래스가 새 블루프린트의 부모가 되며, Actor나 Character 같은 엔진 클래스뿐 아니라 직접 만든 블루프린트도 부모로 고를 수 있습니다.

모든 문이 공통으로 가지는 열기와 닫기 기능을 BP_Door_Base라는 블루프린트에 만들어 두었다고 해 봅시다. 나무 문과 철문을 만들고 싶다면 BP_Door_Wood와 BP_Door_Metal을 만들 때 부모 클래스로 BP_Door_Base를 고릅니다. 이렇게 만든 두 자식은 BP_Door_Base의 열기와 닫기 기능, 변수, 컴포넌트를 모두 물려받고, 여기에 각자의 시각 요소(나무 모델, 철 모델)나 추가 기능(철문의 자물쇠)을 더할 수 있습니다.


물려받은 함수를 오버라이드하기

자식 클래스는 부모에게서 물려받은 함수나 이벤트를 오버라이드, 즉 재정의해서 자신만의 방식으로 동작하게 할 수 있습니다. 오버라이드는 부모의 기본 동작을 완전히 대신할 때도 쓰고, 부모의 동작을 실행한 뒤 자식의 처리를 덧붙일 때도 씁니다. 중요한 점은 오버라이드한 그래프에서 부모 호출을 빼면 부모 구현이 자동으로 실행되지 않는다는 것입니다.

오버라이드하려면 자식 블루프린트 에디터를 열고, 마이 블루프린트(My Blueprint) 패널의 함수(Functions) 항목에 있는 Override 목록에서 재정의할 부모 함수를 고릅니다. 출력이 없는 함수는 함수 그래프가 아니라 이벤트 그래프의 이벤트로 나타날 수 있으므로, 생성된 함수나 이벤트의 진입점에 로직을 연결합니다. Event BeginPlay처럼 원래 이벤트인 것은 자식의 이벤트 그래프에 그 이벤트를 배치하면 오버라이드가 됩니다.

부모의 원래 로직도 실행하고 싶다면 부모 호출 노드를 씁니다. 오버라이드한 함수나 이벤트의 진입점 노드를 오른쪽 클릭해 Add Call to Parent Function을 고르면 Parent: 함수이름 노드가 생기고, 이 노드를 실행 선에 넣은 위치에서 부모 구현이 실행됩니다. 자식의 추가 로직보다 부모의 BeginPlay를 먼저 실행하고 싶다면 진입점 바로 뒤에 부모 호출을 두는 식입니다. 그래서 오버라이드를 설계할 때는 무엇을 그대로 물려받고, 무엇을 새로 정의하며, 부모 로직을 어느 순서로 함께 실행할지를 차례로 정하는 것이 좋습니다.


나무 문으로 확인하는 세 가지 결과

오버라이드와 부모 호출을 작은 예제로 확인해 보겠습니다. 아직 부모 에셋이 없다면 Actor를 부모로 BP_Door_Base를 만들고, 출력 매개변수가 없는 함수 OpenDoor를 만들어 Print String 하나로 "문이 열립니다!"를 출력하게 합니다. 컴파일하고 저장한 뒤, 이 클래스를 부모로 BP_Door_Wood를 만듭니다. 이번 목표는 두 출력이 언제 실행되는지 확인하는 것이므로 실제 문 메시나 애니메이션은 필요하지 않습니다.

BP_Door_Wood를 열고 함수 항목의 Override 목록에서 OpenDoor를 고른 뒤, 생성된 그래프에 Print String을 이어 "나무 문이 열립니다!"를 출력하게 합니다. 부모의 문구도 함께 출력하고 싶다면 진입점에서 Add Call to Parent Function으로 Parent: OpenDoor를 만들고, 진입점에서 부모 호출로, 부모 호출에서 Print String으로 실행 선을 잇습니다. 확인하려면 자식의 이벤트 그래프에서 BeginPlay 뒤에 OpenDoor를 한 번 호출하게 하고, BP_Door_Wood 인스턴스 하나를 레벨에 배치해 플레이합니다.

오버라이드에서 부모 구현을 호출할지 선택합니다

서로 독립적인 세 구현 중 부모 호출을 연결한 실제 경로에서 자식 진입→Parent OpenDoor 출력→자식 출력 순서를 추적할 수 있다. 재정의 없음/부모 호출 없음 비교는 위 관계도와 표에 유지한다.

INHERIT · OVERRIDE · PARENT CALL

오버라이드에서 부모 구현을 호출할지 선택합니다

부모 BP_Door_Base의 OpenDoor가 “문이 열립니다!”를 출력하는 예입니다. 자식 BP_Door_Wood의 구현 방식에 따라 출력 결과가 달라집니다.

재정의 여부와 부모 호출에 따른 세 경로재정의하지 않으면 상속한 부모 구현이 출력한다. 재정의하고 부모를 호출하지 않으면 자식만 출력한다. 자식 진입점에서 Parent OpenDoor를 실행한 뒤 자식 출력에 연결하면 부모 문구 다음에 자식 문구가 출력된다. 세 경로는 선택한 구현의 비교이지 연속 실행 순서가 아니다.① 재정의 없음상속한 OpenDoor부모 구현 사용부모 문구 출력문이 열립니다!② 재정의 · 부모 호출 없음자식 OpenDoor자식 Print만 연결자식 문구 출력나무 문이 열립니다!③ 부모 호출 후 자식 처리자식 OpenDoor재정의 진입Parent: OpenDoor문이 열립니다!자식 Print나무 문이 열립니다!
재정의 여부와 부모 호출에 따른 세 경로재정의하지 않으면 상속한 부모 구현이 출력한다. 재정의하고 부모를 호출하지 않으면 자식만 출력한다. 자식 진입점에서 Parent OpenDoor를 실행한 뒤 자식 출력에 연결하면 부모 문구 다음에 자식 문구가 출력된다. 세 경로는 선택한 구현의 비교이지 연속 실행 순서가 아니다.① 재정의 없음상속한 OpenDoor부모 구현 사용부모 문구 출력문이 열립니다!② 부모 호출 없는 재정의자식 OpenDoor자식 Print만 연결자식 문구 출력나무 문이 열립니다!③ 부모 호출 후 자식 처리자식 OpenDoor재정의 진입Parent: OpenDoor문이 열립니다!자식 Print나무 문이 열립니다!

재정의된 그래프가 부모 구현을 자동 실행하지는 않습니다. 공통 처리가 필요하면 부모 호출을 연결하고 자식 처리와의 순서를 정합니다. 비교의 세 경로 사이에는 실행선이 없습니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
자식 진입

부모 호출을 연결한 override 사본을 실행한다.

  • 현재: 자식 OpenDoor
  • 부모 호출: 명시적으로 연결
  • 부모 호출 없는 구현과 같은 실행으로 이어 붙이지 않는다.
Parent 호출

명시한 Parent OpenDoor가 부모 출력을 수행한다.

  • 현재: Parent OpenDoor
  • 출력 순서: 부모 먼저
  • 재정의했다고 부모 구현이 자동 실행되는 것은 아니다.
자식 후속

돌아온 뒤 자식 출력을 수행한다.

  • 출력 순서: 부모→자식
  • 현재: 자식 후속
  • 실행 핀의 연결이 이 순서를 만든다.

오버라이드에서 부모 구현을 호출할지 선택합니다

서로 독립적인 세 구현 중 부모 호출을 연결한 실제 경로에서 자식 진입→Parent OpenDoor 출력→자식 출력 순서를 추적할 수 있다. 재정의 없음/부모 호출 없음 비교는 위 관계도와 표에 유지한다.

INHERIT · OVERRIDE · PARENT CALL

오버라이드에서 부모 구현을 호출할지 선택합니다

부모 BP_Door_Base의 OpenDoor가 “문이 열립니다!”를 출력하는 예입니다. 자식 BP_Door_Wood의 구현 방식에 따라 출력 결과가 달라집니다.

재정의 여부와 부모 호출에 따른 세 경로재정의하지 않으면 상속한 부모 구현이 출력한다. 재정의하고 부모를 호출하지 않으면 자식만 출력한다. 자식 진입점에서 Parent OpenDoor를 실행한 뒤 자식 출력에 연결하면 부모 문구 다음에 자식 문구가 출력된다. 세 경로는 선택한 구현의 비교이지 연속 실행 순서가 아니다.① 재정의 없음상속한 OpenDoor부모 구현 사용부모 문구 출력문이 열립니다!② 재정의 · 부모 호출 없음자식 OpenDoor자식 Print만 연결자식 문구 출력나무 문이 열립니다!③ 부모 호출 후 자식 처리자식 OpenDoor재정의 진입Parent: OpenDoor문이 열립니다!자식 Print나무 문이 열립니다!
재정의 여부와 부모 호출에 따른 세 경로재정의하지 않으면 상속한 부모 구현이 출력한다. 재정의하고 부모를 호출하지 않으면 자식만 출력한다. 자식 진입점에서 Parent OpenDoor를 실행한 뒤 자식 출력에 연결하면 부모 문구 다음에 자식 문구가 출력된다. 세 경로는 선택한 구현의 비교이지 연속 실행 순서가 아니다.① 재정의 없음상속한 OpenDoor부모 구현 사용부모 문구 출력문이 열립니다!② 부모 호출 없는 재정의자식 OpenDoor자식 Print만 연결자식 문구 출력나무 문이 열립니다!③ 부모 호출 후 자식 처리자식 OpenDoor재정의 진입Parent: OpenDoor문이 열립니다!자식 Print나무 문이 열립니다!

재정의된 그래프가 부모 구현을 자동 실행하지는 않습니다. 공통 처리가 필요하면 부모 호출을 연결하고 자식 처리와의 순서를 정합니다. 비교의 세 경로 사이에는 실행선이 없습니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
자식 진입

부모 호출을 연결한 override 사본을 실행한다.

  • 현재: 자식 OpenDoor
  • 부모 호출: 명시적으로 연결
  • 부모 호출 없는 구현과 같은 실행으로 이어 붙이지 않는다.
Parent 호출

명시한 Parent OpenDoor가 부모 출력을 수행한다.

  • 현재: Parent OpenDoor
  • 출력 순서: 부모 먼저
  • 재정의했다고 부모 구현이 자동 실행되는 것은 아니다.
자식 후속

돌아온 뒤 자식 출력을 수행한다.

  • 출력 순서: 부모→자식
  • 현재: 자식 후속
  • 실행 핀의 연결이 이 순서를 만든다.

위 그림은 자식 BP_Door_Wood를 어떻게 구현했느냐에 따라 출력이 달라지는 세 경로를 비교합니다. ① 재정의하지 않으면 자식은 상속한 부모 구현을 그대로 써서 "문이 열립니다!"를 출력합니다. ② 재정의하고 부모를 호출하지 않으면 자식의 Print String만 실행되어 "나무 문이 열립니다!"만 출력됩니다. ③ 자식의 진입점에서 Parent: OpenDoor를 실행한 뒤 자식 출력으로 이으면, 부모 문구 다음에 자식 문구가 출력됩니다. 세 경로는 구현 방식을 나란히 비교한 것이며, 그림에서 경로 사이에 실행 선이 없듯 한 번 플레이에 연달아 실행되는 순서가 아닙니다.

단계 그림은 ③의 경로를 세 단계로 따라갑니다. 먼저 부모 호출을 연결한 오버라이드 그래프의 진입점이 실행되고, 다음으로 명시한 Parent: OpenDoor가 부모의 출력을 실행합니다. 부모 구현이 끝나 흐름이 돌아오면 마지막으로 자식의 출력이 실행됩니다. 그림 아래의 설명처럼, 재정의된 그래프는 부모 구현을 자동으로 실행하지 않으므로 공통 처리가 필요하면 부모 호출을 연결하고 자식 처리와의 순서를 정해야 합니다.

이처럼 공통된 부분은 부모 클래스에 두고 종류마다 다른 부분은 자식 클래스에서 더하거나 재정의하면, 문이 열 종류로 늘어나도 공통 규칙은 한곳에서 관리할 수 있습니다.


정리

  • 상속은 부모 클래스의 변수, 함수, 이벤트, 컴포넌트를 자식 클래스가 물려받는 것이며, 블루프린트는 부모 클래스 선택 창에서 고른 클래스를 부모로 삼습니다.
  • 접근 지정과 편집 허용 여부에 따라 자식이 직접 읽거나 바꿀 수 있는 범위가 다르며, 액터와 컴포넌트처럼 다른 계층끼리는 상속할 수 없습니다.
  • 부모의 구현을 고치면 그대로 쓰는 자식에 반영되지만, 오버라이드한 자식이나 기본값을 덮어쓴 자식은 따로 확인해야 합니다.
  • 오버라이드는 마이 블루프린트 패널의 Override 목록에서 만들고, 부모 구현을 함께 실행하려면 Add Call to Parent Function으로 만든 Parent: 노드를 실행 선에 넣습니다.
  • 오버라이드한 그래프에서 부모 호출을 빼면 부모 구현은 실행되지 않습니다.

연습 문제

1. 부모 호출을 뺀 결과

부모 OpenDoor의 문구를 "공통 문 처리"로 바꾸었습니다. 자식 OpenDoor에는 부모 호출 없이 "나무 문 처리"를 출력하는 노드만 있고, 그 자식 인스턴스에서 OpenDoor를 한 번 호출합니다. 공통 문구도 함께 출력될까요?

해설 보기

자식 문구 "나무 문 처리"만 출력됩니다. 부모 함수를 고쳐도 그 수정이 자식의 별도 재정의 그래프를 실행시키거나, 자식 그래프에 부모 호출을 끼워 넣지는 않습니다. 자식은 이미 자신의 구현으로 OpenDoor를 대신하고 있기 때문입니다.

공통 처리도 필요하다면 자식 그래프에 Parent: OpenDoor를 연결하고, 자식 처리의 앞과 뒤 중 어디에서 실행할지 정합니다. 본문의 원래 두 문구 예제와 이번 변형의 문구를 헷갈리지 않게 구분해서 확인하세요.

2. 재정의하지 않은 철문

같은 상황에서 BP_Door_Metal은 OpenDoor를 오버라이드하지 않았습니다. 부모 문구를 "공통 문 처리"로 바꾼 뒤 철문 인스턴스에서 OpenDoor를 호출하면 무엇이 출력될까요?

해설 보기

"공통 문 처리"가 출력됩니다. BP_Door_Metal은 OpenDoor를 재정의하지 않았으므로 부모의 구현을 그대로 쓰고, 부모를 고친 내용이 컴파일 뒤 바로 반영됩니다.

1번과 비교하면, 부모의 수정이 반영되는지는 자식이 그 함수를 재정의했는지에 달려 있다는 것을 알 수 있습니다. 부모를 고친 뒤에는 재정의한 자식이 있는지 찾아 의도대로 동작하는지 함께 확인합니다.

3. 부모 호출의 위치 바꾸기

③의 그래프에서 실행 선을 바꿔, 진입점에서 자식의 Print String을 먼저 실행하고 그 뒤에 Parent: OpenDoor를 실행하게 했습니다. 두 문구는 어떤 순서로 출력될까요?

해설 보기

"나무 문이 열립니다!"가 먼저, "문이 열립니다!"가 나중에 출력됩니다. 부모 호출 노드는 실행 선에서 놓인 위치에 따라 실행되는 일반 노드처럼 동작하므로, 자식 처리 뒤에 두면 부모 구현도 뒤에 실행됩니다.

그래서 부모가 초기화하는 값을 자식이 써야 한다면 부모 호출을 앞에, 자식이 먼저 값을 준비하고 부모가 그 값을 써야 한다면 부모 호출을 뒤에 두는 식으로 순서를 정합니다.

부모 클래스의 종류와 블루프린트 상속의 예는 언리얼 엔진 공식 문서의 블루프린트 클래스 설명에서 확인할 수 있습니다.

다음 글 블루프린트 인터페이스 활용에서는 상속 관계가 없는 여러 클래스가 같은 요청에 각자 다르게 반응하도록 만드는 블루프린트 인터페이스를 살펴봅니다.