[에러 해결] Python 파일 쓰기 함수에서 동적 객체 속성 접근 및 조건부 할당 오류 해결

안녕하세요! 구글 SEO와 사용자 경험(UX) 최적화에 정통한 기술 블로그 에디터입니다. 오늘은 Python 코드에서 파일 쓰기 기능을 구현하면서 자주 발생하는 논리적 오류와 동적 객체 속성 접근 문제에 대해 깊이 있게 다뤄보겠습니다. 이 문제는 특히 여러 종류의 객체에서 특정 ID 값을 추출하여 파일에 저장해야 할 때 개발자들이 겪기 쉬운 부분입니다.

1. 에러 발생 상황

사용자분께서는 주어진 파일 이름에 따라 객체의 특정 속성(예: user_id, test_1_id 등)을 동적으로 추출하여 파일에 저장하는 기능을 구현하려 했습니다. 하지만 기대와 달리 파일에 아무런 ID도 저장되지 않는 문제가 발생했습니다. 다음은 문제의 원인이 된 코드 스니펫입니다.

def write_to_file(
    file_name: str,
    list_name: list,
):
    """This function is to write id's to file.

    It creates file with given file_name parameter and it loops through the list
    that we pass from another function.
    """
    try:
        id_to_write = ""
        if file_name == "dummy_test1_datas.txt":
            id_to_write == "test_1_id" # 할당이 아닌 비교 연산자 사용
        elif file_name == "dummy_test2_datas.txt":
            id_to_write == "test_2_id" # 할당이 아닌 비교 연산자 사용
        elif file_name == "dummy_test3_datas.txt":
            id_to_write == "test_3_id" # 할당이 아닌 비교 연산자 사용
        print(id_to_write) # 항상 빈 문자열이 출력됨
        with open(file_name, "w", encoding="utf-8", newline="") as f:
            for data in list_name:
                f.writelines(
                    data.user_id if file_name == "dummy_user_datas.txt" else data # 동적 속성 접근 문제
                )
        print(f"Alle toegevoegde id's zijn opgeslagen in {file_name} bestand.")
    except:
        print("Probleem bij het schrijven naar een bestand.")

이 코드는 다음과 같은 문제를 겪고 있었습니다:

  • id_to_write 변수에 원하는 속성 이름이 할당되지 않음.
  • f.writelines 내부에서 file_name에 따라 객체(data)의 특정 속성(예: data.test_1_id)을 동적으로 접근하는 방법이 잘못됨.

2. 명확한 발생 원인

이 문제의 핵심 원인은 크게 두 가지입니다.

2.1. 할당(=)과 비교(==) 연산자 혼동

코드의 다음 부분에서 id_to_write 변수에 값을 할당하려 했으나, 실제로는 비교 연산자 ==를 사용했습니다.

        if file_name == "dummy_test1_datas.txt":
            id_to_write == "test_1_id" # 이 부분에서 '=' 대신 '==' 사용

이 때문에 id_to_write 변수는 초기값인 빈 문자열("")을 계속 유지하게 됩니다. 결과적으로 print(id_to_write)는 항상 빈 문자열을 출력합니다.

2.2. 동적 객체 속성 접근의 부재

사용자의 요구사항은 file_name에 따라 data.user_id, data.test_1_id와 같이 객체(data)의 속성을 동적으로 변경하여 접근하는 것이었습니다. 그러나 원본 코드의 f.writelines 부분은 다음과 같이 작성되어 있었습니다.

                f.writelines(
                    data.user_id if file_name == "dummy_user_datas.txt" else data
                )

이 코드는 "dummy_user_datas.txt"인 경우에만 data.user_id에 접근하고, 그 외의 경우에는 data 객체 자체를 파일에 쓰려고 합니다. data 객체가 직접 쓰기 가능한 문자열이 아니라면, 원하는 ID가 저장되지 않거나 타입 에러가 발생할 수 있습니다. Python에서 객체의 속성 이름을 문자열로 가지고 있을 때 해당 속성에 동적으로 접근하려면 getattr() 함수를 사용해야 합니다.

3. 해결 방법 및 코드 예시

위에서 언급된 두 가지 주요 원인을 해결하여 코드를 개선할 수 있습니다.
핵심은 올바른 할당 연산자 사용과 getattr()을 이용한 동적 속성 접근입니다.

3.1. 해결 코드 예시

def write_to_file(
    file_name: str,
    list_name: list,
):
    """이 함수는 ID를 파일에 작성합니다.

    지정된 파일 이름으로 파일을 생성하고, 다른 함수에서 전달받은
    리스트를 순회하며 내용을 작성합니다.
    """
    # 파일 이름에 따라 추출할 속성 이름을 매핑합니다.
    # 사용자 요구사항에 맞춰 'user_id', 'test_1_id' 등을 동적으로 결정합니다.
    attribute_to_extract_map = {
        "dummy_test1_datas.txt": "test_1_id",
        "dummy_test2_datas.txt": "test_2_id",
        "dummy_test3_datas.txt": "test_3_id",
        "dummy_user_datas.txt": "user_id", # user_id 조건에 맞춰 추가
        # 다른 파일 이름에 대한 매핑이 있다면 여기에 추가할 수 있습니다.
    }

    try:
        # 파일 이름을 기반으로 추출할 속성 이름을 가져옵니다.
        # 매핑에 없는 파일 이름의 경우 None을 반환합니다.
        target_attribute_name = attribute_to_extract_map.get(file_name)

        with open(file_name, "w", encoding="utf-8", newline="") as f:
            for data_item in list_name: # 'data' 변수명 충돌을 피하기 위해 'data_item'으로 변경
                value_to_write = ""
                if target_attribute_name:
                    # getattr()을 사용하여 data_item 객체에서 target_attribute_name에 해당하는 속성을 동적으로 가져옵니다.
                    # 만약 해당 속성이 없으면, data_item 객체 자체를 기본값으로 사용합니다 (원본 코드의 'else data' 로직 반영).
                    extracted_value = getattr(data_item, target_attribute_name, data_item)
                    value_to_write = str(extracted_value) # 파일에 쓰기 위해 문자열로 변환
                else:
                    # 매핑된 속성 이름이 없는 경우 (target_attribute_name이 None)
                    # 또는 원본 코드의 'else data' 로직을 따르기 위해 data_item 자체를 씁니다.
                    value_to_write = str(data_item) # 파일에 쓰기 위해 문자열로 변환

                # 각 ID 뒤에 새 줄을 추가하여 파일에 쓰기
                f.write(value_to_write + '\n')

        print(f"Alle toegevoegde id's zijn opgeslagen in {file_name} bestand.")
    except Exception as e: # 좀 더 구체적인 예외를 잡는 것이 좋습니다.
        print(f"파일에 쓰는 중 문제가 발생했습니다: {e}")

3.2. 코드 변경 설명

  1. 속성 이름 매핑 (attribute_to_extract_map):

    if/elif 문의 복잡성을 줄이고 가독성을 높이기 위해 파일 이름과 추출할 속성 이름을 딕셔너리로 매핑했습니다. 이를 통해 어떤 파일에 어떤 ID를 저장해야 하는지 명확하게 파악할 수 있으며, 새로운 파일 종류가 추가될 때도 딕셔너리에 한 줄만 추가하면 되어 유지보수가 용이합니다.

  2. getattr()을 사용한 동적 속성 접근:

    파일 이름에 따라 결정된 target_attribute_name 문자열을 getattr(data_item, target_attribute_name, data_item)와 같이 사용하여 data_item 객체에서 해당 이름의 속성을 동적으로 가져옵니다. 만약 data_item에 해당 속성이 없다면, getattr()의 세 번째 인자인 data_item이 기본값으로 반환되어 원본 코드의 else data와 유사한 폴백(fallback) 동작을 합니다.

  3. 문자열 변환 (str()):

    파일에 쓰는 모든 값은 문자열이어야 합니다. 따라서 extracted_value나 data_item이 문자열이 아닐 경우를 대비하여 str() 함수를 사용하여 명시적으로 문자열로 변환했습니다.

  4. 개별 라인 쓰기 (f.write + '\n'):

    f.writelines()는 문자열들의 리스트를 인자로 받아 각각을 파일에 씁니다. 하지만 각 요소 뒤에 자동으로 줄바꿈을 추가하지 않습니다. 단일 값을 쓰고 줄바꿈을 추가하는 일반적인 패턴은 f.write(value + '\n')입니다. 이 방식이 명확하고 의도에 잘 맞습니다.

  5. 구체적인 예외 처리:

    except: 대신 except Exception as e:를 사용하여 어떤 예외가 발생했는지 구체적인 오류 메시지를 출력하도록 개선했습니다. 이는 문제 발생 시 디버깅에 큰 도움이 됩니다.

4. 향후 예방을 위한 팁

유사한 에러를 방지하고 더 견고한 코드를 작성하기 위한 몇 가지 팁입니다.

4.1. 할당(=)과 비교(==) 연산자 숙지

가장 기본적인 실수 중 하나이지만, 초보 개발자뿐만 아니라 숙련된 개발자도 급하게 코드를 작성하다 보면 혼동할 수 있습니다. 변수에 값을 저장할 때는 항상 =를, 두 값이 같은지 비교할 때는 ==를 사용하세요.

4.2. 동적 속성 접근을 위한 getattr() 활용

객체의 속성 이름을 문자열로 가지고 있을 때 해당 속성에 접근해야 한다면 getattr(object, name, default_value)를 사용하세요. 이는 코드의 유연성을 높이고 조건문을 단순화하는 데 크게 기여합니다.

4.3. 조건이 많아질 경우 매핑(Mapping) 사용

if/elif/else 조건문이 많아지고 복잡해질 때, 딕셔너리와 같은 매핑 구조를 사용하면 코드의 가독성과 확장성을 크게 개선할 수 있습니다. 이 글의 해결 코드처럼 파일 이름-속성 이름 매핑을 사용하면 코드 변경 없이 새로운 파일 유형을 쉽게 추가할 수 있습니다.

4.4. 명확하고 구체적인 예외 처리

try-except 블록을 사용할 때는 단순히 except:만 사용하는 것보다 except Exception as e:와 같이 구체적인 예외를 잡고 오류 메시지를 출력하는 것이 디버깅에 훨씬 효과적입니다. 사용자에게 문제가 발생했음을 알리는 동시에 개발자에게 문제의 원인을 파악할 수 있는 정보를 제공합니다.

이 문서를 통해 Python 파일 쓰기 기능 구현 시 발생할 수 있는 흔한 논리적 오류와 동적 속성 접근 문제에 대한 이해를 높이고, 더 효율적이고 견고한 코드를 작성하는 데 도움이 되기를 바랍니다. 궁금한 점이 있다면 언제든지 질문해주세요!

댓글 남기기