메인 콘텐츠로 건너뛰기

간단한 예제

다음은 이미지 반전 노드의 코드로, 맞춤형 노드 개발의 핵심 개념을 살펴볼 수 있습니다.
class InvertImageNode:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": { "image_in" : ("IMAGE", {}) },
        }

    RETURN_TYPES = ("IMAGE",)
    RETURN_NAMES = ("image_out",)
    CATEGORY = "examples"
    FUNCTION = "invert"

    def invert(self, image_in):
        image_out = 1 - image_in
        return (image_out,)

주요 속성

모든 맞춤형 노드는 Python 클래스이며, 다음과 같은 주요 속성을 갖습니다:

INPUT_TYPES

이름에서 알 수 있듯이 INPUT_TYPES는 노드의 입력을 정의합니다. 이 메서드는 반드시 required 키를 포함한 dict를 반환하며, optional 및/또는 hidden 키도 포함할 수 있습니다. requiredoptional 입력의 유일한 차이점은 optional 입력은 연결되지 않은 채로 둘 수 있다는 것입니다. hidden 입력에 대한 자세한 내용은 숨겨진 입력을 참조하세요. 각 키의 값으로는 또 다른 dict가 있으며, 여기서 키-값 쌍은 입력의 이름과 유형을 지정합니다. 유형은 tuple로 정의되며, 첫 번째 요소는 데이터 유형을, 두 번째 요소는 추가 매개변수의 dict입니다. 여기서는 image_in이라는 이름의 필수 입력 하나만 있고, 유형은 IMAGE이며 추가 매개변수는 없습니다. 다음 몇 가지 속성과 달리, 이 INPUT_TYPES@classmethod입니다. 이렇게 하면 드롭다운 위젯의 옵션들(예를 들어 로드할 체크포인트의 이름)을 Comfy가 실행 시점에 계산할 수 있도록 합니다. 이에 대해서는 나중에 더 자세히 설명하겠습니다.

RETURN_TYPES

노드가 반환하는 데이터 유형을 정의하는 strtuple. 노드에 출력이 없더라도 이는 반드시 제공되어야 합니다. RETURN_TYPES = ()
출력이 딱 하나인 경우, 뒤에 쉼표를 기억하세요: RETURN_TYPES = ("IMAGE",). 이는 Python이 이를 tuple로 인식하도록 하기 위해 필요합니다.

RETURN_NAMES

출력을 레이블링하는 데 사용될 이름들입니다. 선택사항이며, 생략하면 이름은 단순히 RETURN_TYPES를 소문자로 바꾼 것입니다.

CATEGORY

ComfyUI 노드 추가 메뉴에서 노드를 찾을 수 있는 위치입니다. 하위 메뉴는 경로로 지정할 수 있으며, 예를 들어 examples/trivial처럼 가능합니다.

FUNCTION

노드가 실행될 때 호출해야 하는 클래스 내 Python 함수의 이름입니다. 함수는 명명된 인수로 호출됩니다. 모든 required(및 hidden) 입력이 포함되며, optional 입력은 연결된 경우에만 포함되므로 함수 정의에서 기본값을 제공하거나 **kwargs로 포착해야 합니다. 함수는 RETURN_TYPES에 해당하는 튜플을 반환합니다. 아무것도 반환하지 않아도 이는 반드시 필요합니다(return ()). 다시 한번 말씀드리지만, 출력이 하나뿐인 경우 뒤에 쉼표를 기억하세요: return (image_out,)!

실행 제어 추가 기능

Comfy의 훌륭한 기능 중 하나는 출력을 캐시하고, 이전 실행과 다른 결과를 낼 수 있는 노드만 실행한다는 점입니다. 이는 많은 워크플로우를 크게 가속화할 수 있습니다. 본질적으로 이는 어떤 노드가 출력을 생성하는지 식별하고(특히 이미지 미리보기 및 이미지 저장 노드는 항상 실행됨), 이후 역방향으로 작업하여 마지막 실행 이후 변경되었을 수 있는 데이터를 제공하는 노드를 식별합니다. 맞춤형 노드의 두 가지 선택적 기능이 이 과정을 돕습니다.

OUTPUT_NODE

기본적으로 노드는 출력으로 간주되지 않습니다. OUTPUT_NODE = True로 설정하면 출력임을 명시할 수 있습니다.

IS_CHANGED

기본적으로 Comfy는 노드의 입력이나 위젯이 변경되면 노드가 변경되었다고 간주합니다. 일반적으로 이는 정확하지만, 예를 들어 노드가 난수를 사용하거나(시드를 지정하지 않는 것이 좋으며, 이 경우 사용자가 재현성을 제어하고 불필요한 실행을 피할 수 있도록 시드 입력을 제공하는 것이 좋습니다), 외부에서 변경될 수 있는 입력을 로드하거나, 때때로 입력을 무시하는 경우(그래서 입력이 변경되었다고 해서 실행할 필요가 없는 경우) 이 기능을 오버라이드해야 할 수 있습니다.
이름에도 불구하고, IS_CHANGED는 bool을 반환해서는 안 됩니다
IS_CHANGEDFUNCTION에 의해 정의된 메인 함수와 동일한 인수를 전달받으며, 임의의 Python 객체를 반환할 수 있습니다. 이 객체는 이전 실행에서 반환된 것과 비교되며, is_changed != is_changed_old인 경우 노드가 변경된 것으로 간주됩니다(이 코드는 execution.py에 있으니 필요하다면 확인해 보세요). True == True이므로, 변경되었다고 True를 반환하는 노드는 변경되지 않은 것으로 간주됩니다! 이는 Comfy 코드를 변경하면 기존 노드가 깨질 수 있기 때문에 그렇게 되지 않을 거라 확신합니다. 노드가 항상 변경된 것으로 간주되도록 지정하려면(가능하면 피해야 함, Comfy가 실행할 내용을 최적화하는 것을 막기 때문), return float("NaN")을 반환하세요. 이는 NaN 값을 반환하며, 이는 다른 NaN과도 같지 않습니다. 실제로 변경 여부를 확인하는 좋은 예는 내장된 LoadImage 노드의 코드로, 이미지를 로드하고 해시를 반환합니다.
    @classmethod
    def IS_CHANGED(s, image):
        image_path = folder_paths.get_annotated_filepath(image)
        m = hashlib.sha256()
        with open(image_path, 'rb') as f:
            m.update(f.read())
        return m.digest().hex()

SEARCH_ALIASES

선택사항. 사용자가 이 노드를 찾을 때 검색할 수 있는 대체 이름 목록입니다. 이는 /object_info API 응답에서 search_aliases로 포함됩니다.
SEARCH_ALIASES = ["text concat", "join text", "merge strings"]

기타 속성

노드의 기본 Comfy 처리를 수정하는 데 사용할 수 있는 세 가지 속성이 더 있습니다.

INPUT_IS_LIST, OUTPUT_IS_LIST

이들은 데이터의 순차적 처리를 제어하는 데 사용되며, 나중에 설명됩니다.

VALIDATE_INPUTS

클래스 메서드 VALIDATE_INPUTS가 정의되면 워크플로우가 실행되기 전에 호출됩니다. VALIDATE_INPUTS는 입력이 유효한 경우 True를 반환하거나, 오류를 설명하는 메시지(문자열 형태)를 반환해야 합니다(이 경우 실행이 방지됩니다).

상수 검증

VALIDATE_INPUTS는 워크플로우 내에서 상수로 정의된 입력만 받습니다. 다른 노드로부터 받는 입력은 VALIDATE_INPUTS에서 사용할 수 없습니다.
VALIDATE_INPUTS는 서명이 요청하는 입력만 받습니다(즉, inspect.getfullargspec(obj_class.VALIDATE_INPUTS).args에서 반환된 입력). 이런 방식으로 받는 입력은 기본 검증 규칙을 통과하지 않습니다. 예를 들어 다음 코드 조각에서는 프론트엔드가 foo 입력의 지정된 minmax 값을 사용하지만 백엔드는 이를 강제하지 않습니다.
class CustomNode:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": { "foo" : ("INT", {"min": 0, "max": 10}) },
        }

    @classmethod
    def VALIDATE_INPUTS(cls, foo):
        # YOLO, 무엇이든 가능!
        return True
또한, 함수가 **kwargs 입력을 받는 경우, 가능한 모든 입력을 받게 되며, 이 모든 입력은 명시적으로 지정된 것처럼 검증을 건너뜁니다.

유형 검증

VALIDATE_INPUTS 메서드가 input_types라는 이름의 인수를 받으면, 각 입력의 이름이 다른 노드의 출력과 연결된 상태에서 그 출력의 유형을 나타내는 딕셔널이 전달됩니다. 이 인수가 존재하면 기본 입력 유형 검증은 모두 건너뜁니다. 다음은 프론트엔드가 여러 유형을 지정할 수 있다는 사실을 활용한 예시입니다:
class AddNumbers:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "input1" : ("INT,FLOAT", {"min": 0, "max": 1000})
                "input2" : ("INT,FLOAT", {"min": 0, "max": 1000})
            },
        }

    @classmethod
    def VALIDATE_INPUTS(cls, input_types):
        # input1과 input2의 min과 max는 여전히 검증됩니다
        # 우리는 input1과 input2를 인수로 받지 않았기 때문입니다
        if input_types["input1"] not in ("INT", "FLOAT"):
            return "input1은 INT 또는 FLOAT 유형이어야 합니다"
        if input_types["input2"] not in ("INT", "FLOAT"):
            return "input2은 INT 또는 FLOAT 유형이어야 합니다"
        return True