업데이트 2026년 10월 15일읽는 시간 약 6분

한 화면엔 3D 건물 와이어프레임, 다른 화면엔 코드가 떠 있는 개발 환경
한 화면엔 3D 건물 와이어프레임, 다른 화면엔 코드가 떠 있는 개발 환경 — AI로 만든 예시 이미지

Revit API로 무언가를 하려면 거의 항상 첫 단계가 같다. 모델에서 원하는 요소를 골라내는 것이다. 벽만, 특정 레벨의 문만, 배치된 인스턴스만. 이 조회를 담당하는 클래스가 FilteredElementCollector다. 그리고 골라낸 요소의 값을 바꾸려면 반드시 Transaction 안에서 해야 한다. 이 두 가지, “필터로 모으고 트랜잭션으로 고친다”는 패턴은 Revit 플러그인 코드의 8할을 차지한다.

조회와 수정은 구분해서 처리합니다
조회와 수정은 구분해서 처리합니다

■ FilteredElementCollector의 기본형

컬렉터는 생성자에 Document를 넘기고 필터 메서드를 체인으로 이어 붙이는 방식으로 쓴다. 마지막에 ToElements()나 ToElementIds()로 결과를 뽑는다.

// 배치된 벽 인스턴스만 모으기
var walls = new FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Walls)   // 카테고리: 벽
    .WhereElementIsNotElementType()          // 타입 제외, 인스턴스만
    .ToElements();

WhereElementIsNotElementType()를 빼면 벽 타입(WallType)까지 딸려 나온다. 인스턴스만 필요하면 반드시 붙이고, 타입 정의만 모을 때는 WhereElementIsElementType()를 쓴다. 런타임 클래스 기준으로 걸러야 하면 OfClass(typeof(Wall))을 붙인다.

■ 필터의 종류: 빠른 필터와 느린 필터

Revit의 요소 필터는 크게 세 갈래다. 성능을 좌우하는 것은 Quick(빠른) 필터냐 Slow(느린) 필터냐의 구분이다. 빠른 필터는 요소의 헤더 정보만 읽어 통과 여부를 판단하므로 요소를 메모리로 완전히 펼치지 않아도 된다. 느린 필터는 각 요소를 메모리에 전개해야 판단할 수 있어 비용이 크다.

분류 대표 필터/메서드 특징
빠른(Quick) OfCategory / OfCategoryId 카테고리로 거름, 헤더만 읽음
빠른(Quick) OfClass 런타임 클래스로 거름
빠른(Quick) WhereElementIsNotElementType / IsElementType 인스턴스/타입 구분
느린(Slow) ElementParameterFilter 파라미터 값 조건, 요소 전개 필요
느린(Slow) ElementLevelFilter, 기하 기반 필터 레벨·교차 등, 비용 큼
논리 LogicalAndFilter / LogicalOrFilter 필터 조합

원칙은 하나다. 빠른 필터로 최대한 범위를 좁힌 뒤에 느린 필터나 LINQ를 적용한다. OfCategory 같은 단축 메서드는 모두 빠른 필터에 대응하므로, 이것부터 걸어 후보 집합을 줄이면 느린 조건이 훑을 요소 수가 확 준다. 처음부터 LINQ의 Where()로 전체를 훑는 코드는 대형 모델에서 눈에 띄게 느려진다.

벽·바닥·기둥이 분리된 3D 건물 모델 분해도
벽·바닥·기둥이 분리된 3D 건물 모델 분해도 — AI로 만든 예시 이미지

■ 성능을 살리는 조회 예시

“1층에 있는, 높이가 3000mm를 넘는 벽”을 찾는다고 하자. 빠른 필터로 벽 인스턴스를 먼저 좁히고, 값 조건은 그 뒤에 건다.

// 빠른 필터로 벽 인스턴스만 확보 (여기서 대부분 걸러진다)
var collector = new FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Walls)
    .WhereElementIsNotElementType();

// 값 조건(느림)은 좁혀진 집합에만 적용
double limitFt = UnitUtils.ConvertToInternalUnits(3000, UnitTypeId.Millimeters);
var tallWalls = collector
    .Where(w => {
        var p = w.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM);
        return p != null && p.AsDouble() > limitFt;
    })
    .ToList();

주의할 점: 하나의 컬렉터 인스턴스는 결과를 한 번 소비하면 재사용이 어렵다. 같은 조건을 여러 번 쓸 거라면 ToElements()로 리스트를 뽑아 두고 그 리스트를 재사용한다. 또 Revit 내부 길이 단위는 피트이므로 mm 값을 비교할 때는 위처럼 UnitUtils로 환산해야 값이 어긋나지 않는다.

■ Transaction: 시작·커밋·롤백

모델을 바꾸는 모든 수정은 트랜잭션 안이어야 한다. 밖에서 값을 바꾸려 하면 예외로 막힌다. 트랜잭션은 Start()로 열고 Commit()으로 확정하며, 문제가 생기면 RollBack()으로 통째로 되돌린다. 사용자 입장에선 이 한 덩어리가 실행 취소(Undo) 한 단계로 묶인다.

using (Transaction t = new Transaction(doc, "벽 주석 일괄 입력"))
{
    t.Start();
    try
    {
        foreach (Element w in tallWalls)
        {
            Parameter cmt = w.LookupParameter("Comments");
            if (cmt != null && !cmt.IsReadOnly)
                cmt.Set("검토완료");
        }
        t.Commit();   // 정상 종료 → 하나의 Undo 단위로 확정
    }
    catch (Exception ex)
    {
        if (t.HasStarted() && !t.HasEnded())
            t.RollBack();   // 예외 시 전부 되돌림
        throw;
    }
}

using으로 감싸면 커밋이나 롤백을 빠뜨려도 트랜잭션이 안전하게 정리된다. 여러 트랜잭션을 묶어 한 번에 되돌리고 싶으면 TransactionGroup을, 반복 안에서 성능이 급할 때는 재생성 억제 옵션을 검토한다.

■ 파라미터 수정 시 자주 나는 실수

증상 원인 대응
Set 호출에서 예외 트랜잭션 밖에서 수정 Start~Commit 안으로 이동
LookupParameter가 null 이름 오타 또는 미바인딩 매개변수 존재·바인딩 여부 확인 후 접근
IsReadOnly 매개변수에 Set 계산식/시스템 매개변수 IsReadOnly 검사 후 건너뜀
길이 값이 이상함 피트↔mm 단위 미환산 UnitUtils로 내부 단위 변환
여러 모니터로 대형 3D 건물 모델을 검토하는 엔지니어
여러 모니터로 대형 3D 건물 모델을 검토하는 엔지니어 — AI로 만든 예시 이미지

■ LookupParameter가 하나만 돌려줄 때

같은 이름의 매개변수가 여러 개면 LookupParameter는 그중 하나만 돌려준다. 결과가 예상과 다르면 GetParameters(name)으로 목록을 확인한다. 성능에서 가장 흔한 실수는 반복문 안에서 컬렉터를 새로 만드는 것으로, 조회는 반복 밖에서 한 번만 하고 결과 리스트를 돈다. “빠른 필터 → 값 조건 → 트랜잭션 수정” 구조는 pyRevit이나 Dynamo 파이썬 노드에서도 문법만 바뀔 뿐 그대로 쓰인다.

BuiltInCategory·BuiltInParameter 열거형과 단위 식별자(UnitTypeId 등)는 Revit 버전에 따라 바뀌어 왔으므로, 배포 전 대상 버전의 API 문서와 대조하고 사본 모델에서 먼저 실행해 결과를 확인한다.


참고 문헌

자주 묻는 질문

FilteredElementCollector는 어떤 역할을 하나요?

Revit 모델에서 원하는 요소를 골라내는 조회를 담당하는 클래스입니다. 생성자에 Document를 넘기고 필터 메서드를 체인으로 이어 붙인 뒤 ToElements()나 ToElementIds()로 결과를 뽑습니다.

빠른 필터와 느린 필터는 무엇이 다른가요?

요소를 메모리에 전개하느냐가 다릅니다. 빠른 필터는 요소의 헤더 정보만 읽어 판단하고, 느린 필터는 각 요소를 메모리에 전개해야 해서 비용이 큽니다. 빠른 필터로 범위를 좁힌 뒤 느린 필터나 LINQ를 적용하는 것이 원칙입니다.

Revit API에서 트랜잭션 없이 값을 바꿀 수 있나요?

바꿀 수 없습니다. 모델을 바꾸는 모든 수정은 트랜잭션 안이어야 하며 밖에서 값을 바꾸려 하면 예외로 막힙니다. Start()로 열고 Commit()으로 확정하며, 문제가 생기면 RollBack()으로 통째로 되돌립니다.

mm 값으로 비교했는데 길이가 이상하게 나오는 이유는 무엇인가요?

단위를 환산하지 않았기 때문입니다. Revit 내부 길이 단위는 피트이므로, mm 값을 비교할 때는 UnitUtils로 내부 단위로 환산해야 값이 어긋나지 않습니다.

같은 주제의 글