늦은 24년 회고록 - 책 "불안 세대"를 읽고 나서

전 글을 보시면 아시겠지만 1월부터 계약직 인턴 생활을 시작했습니다.

출근을 하고 일주일 정도는 도메인 지식 파악과, 기존 코드 분석만 하는데 시간을 사용했습니다.

 

여러 단계로 중첩된 연관관계를 가진 도메인과 비즈니스에 대한 이해를 해야했고, 처음 보는 DDD-Lite를 사용한 코드를 이해하기 위해 일주일 동안 인텔리제이를 붙잡고 있었습니다. 그러던 와중 어느날 갑자기 프론트 쪽 사무실에서 나와선 안되는 소리가 나왔습니다.

 

"…어? 뭐야 이거 왜 안돼”


어느날 파일 업로드가 안된다는 소식이 들려왔다.

프론트 쪽에서 갑자기 txt 파일이 요청에 들어가면 파일 업로드 API가 제대로 동작하지 않는다는 소식이 전해졌습니다.

현재 회사에선 파일 업로드를 위해 AWS-S3와 Presiengd-URL을 이용해 파일 업로드를 처리 중입니다.

 

https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/using-presigned-url.html

 

미리 서명된 URL을 통해 객체 다운로드 및 업로드 - Amazon Simple Storage Service

임시 보안 인증 정보를 사용하여 미리 서명된 URL을 생성하면 보안 인증 정보가 만료되면 이 URL도 만료됩니다. 일반적으로 미리 서명된 URL은 URL을 만드는 데 사용한 자격 증명이 취소, 삭제 또는

docs.aws.amazon.com

(정확하게는 AWS S3가 아닌 회사 내부 NAS를 스토리지로 이용하고 있지만, 이 글에선 S3라고 생각해도 문제는 없습니다)

 

파일 업로드를 위해선 크게 다음과 같은 과정을 거칩니다.

  1. 프론트에서 업로드할 파일 정보를 넣어 백엔드 API에 요청
  2. 백엔드에선 해당 파일 정보를 DB에 저장한 후, 파일 정보에 맞는 presigned url 발급
  3. 프론트에서 해당 url을 이용해 S3 파일 업로드

문제는 2번째 과정인 presigned-url을 발급하는 과정에서 올바른 요청임에도 API가 에러를 뱉는 문제가 발생했다는 겁니다.

txt 확장자를 사용한 파일만 말이죠.

 

프론트에선 S3에 파일 업로드를 시도도 하기 전에 문제가 발생했으니 명백한 백엔드의 문제였습니다.

기존 코드에서 파일 업로드와 관련된 로직을 찾아 보기 시작했습니다. 그리고 얼마 안 가 바로 그 원인을 알 수 있었습니다.


하드코딩된 조건문에서 확장자가 누락됐다.

그 전에 파일 업로드 관련 비즈니스 로직에 대해 조금 더 자세히 살펴봅시다.

 

우리는 파일에 대한 정보를 DB에 저장하고 관리할 필요가 있습니다.

그래서 백엔드에선 presigned-url을 발급해주기 전, 해당 파일에 대한 정보를 DB에 저장해야 합니다.

이때 업로드하는 파일의 종류는 두 가지인데 이미지 파일문서 파일 이고, 상세한 요구사항은 다음과 같습니다.

  • 모든 파일은 공통적으로 (이름, 확장자) 정보를 가진다.
  • 이미지 파일의 경우 추가적으로 (해상도, 가로, 세로 길이) 정보를 가진다.
  • 두 파일은 확장자를 이용해 구분한다. jpg 확장자는 이미지 파일, 나머지 확장자 (txt, pdf, xml)들은 문서 파일로 구분한다.

이제 문제가 된 로직을 살펴봅시다. 파일 생성에 관련된 어플리케이션 서비스 로직입니다.

public class FileService {

    public File createFile(FileCreateRequest request) {
        FileExtension extension = request.extension();
        
        if(isFileTypeDocument(extension)){
            createDocumentFile(request);
        } else {
            createImageFile(request);
        }
    }
    
    // 문제의 로직
    private boolean isFileTypeDocument(FileExtension extension) {
        return extension == PDF || extension == XML;
    }
}

 

사용자 요청으로 들어온 파일 정보 중 확장자를 이용해 요청 파일이 이미지 파일 인지, 문서 파일 인지 구분합니다.
이때 확장자를 확인하는 로직은 isFileTypeDocument()를 통해서 진행됩니다.

그 이후 파일 타입에 알맞은 생성 로직을 호출합니다.

 

여러분들은 혹시 해당 함수가 반환하는 조건식에 TXT 타입 확장자가 빠져있다는 사실을 눈치채셨나요..?

isFileTypeDocument()에 들어있어야 할 TXT 확장자가 빠져있어 올바른 파일 타입으로 구분을 못해서 문제가 발생했던 것이었습니다.


문제는 이것뿐만이 아니다.

txt 파일 업로드 문제는 그렇게 위 함수 조건식 마지막에 TXT 조건을 추가하는 것으로 쉽게 해결했습니다.

그런데 이대로 넘어가기엔 뭔가 찜찜한 생각이 듭니다..

  1. 왜 저런 하드코딩된 조건식이 등장하게 된 걸까요?
  2. 그리고 왜 어플리케이션 서비스에 저 코드가 존재하는것이며, 저 로직 말고 비슷한 문제가 발생하는 로직이 또 없을까요?
    (당연히 있었습니다)
  3. 만약 새로운 확장자 타입이나 파일 종류가 추가되어 지원해야 한다면? (ex : mp4 동영상 파일 등)
    위에서 말한 문제가 생길 수 있는 함수들을 모두 찾아다니며 변경해야 하지 않을까요?

이 시점에서 저를 회사에 소개시켜준 A씨가 저에게 첫 임무를 줬습니다.

저 문제를 해결한 후, 파일 도메인 코드들을 알잘딱 하게 리팩토링해봐라.

 


 

기존 구현 뜯어보기 - JPA 상속 관계 매핑

이번에 문제가 된 로직을 리팩토링 하기 전에 파일 도메인이 어떻게 구현되어 있는지 확인해봅시다.

domain
├── file
│   ├── controller
│   ├── application
│   ├── dao
│   └── domain
│         ├── File.java 
│         ├── ImageFile.java
│         ├── DocumentFile.java
│         ├── FileExtension.java
│         └── ...
└── ...

 

파일 도메인에 들어가 domain 패키지를 열자 DDD 특유의 작은 애그리거트 루트가 아닌, 여러 종류의 파일들이 저를 반겼습니다.

한 번 자세한 구현을 보겠습니다.

 

@Entity @Getter
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class File {
    
    @Id @Column(name = "file_id")
    private Long id;
    private String name;
    
    @Enumerated(EnumType.STRING)
    private FileExtension extension;
}

public enum FileExtension {
    JPG(".jpg"), TXT("txt"), PDF("pdf"), XML("xml");

    private String value;
}
@Entity @Getter
public class ImageFile extends File {

    private double resolution;
    private int width;
    private int height;
}
@Entity
public class DocumentFile extends File {}

 

파일 도메인은 JPA 싱글 테이블 상속 전략을 이용해 구현하고 있었습니다.

파일 도메인의 애그리거트 루트는 File 이었고, File을 상속 받은 ImageFIle, DocumentFile 가 존재했습니다.

 

요구사항을 다시 한 번 생각해보면

  1. 두 종류의 파일 중 한 파일만 가지는 정보가 존재합니다. (이미지 파일은 해상도, 가로, 세로 길이를 저장할 필요가 있음)
  2. 하지만 두 파일을 시스템 내부에서만 구분하고 사용자는 구분하지 않습니다. (요청과 응답 양식은 이미지 파일, 문서 파일 모두 동일함)

위의 두 가지 요구사항 모두를 만족하기 위해 JPA 상속 관계 매핑을 사용해 구현한 것으로 보입니다.

 

파일과 이미지 파일, 문서 파일의 관계가 정확히 (is a / is a kind of) 관계를 만족하기 때문에
상속 관계 매핑을 이용하기로 한 선택은 괜찮은 것처럼 보입니다.

 

굳이 상속 관계 매핑을 사용하지 않고 구현할 방법은 없을까 싶지만

  • 파일 처리와 관련된 비즈니스 로직이 두 파일 모두 동일한데 둘을 다른 애그리거트로 보고 분리하는 것은 오히려 복잡성을 늘리고 중복 코드를 유발할 것으로 보이며
  • 상속 없이 단일 테이블과 단일 엔티티로 제작시 문서 파일에서 getWidth() 함수를 사용할 수 있다는 점이 어색하게 느껴지기도 합니다.

그러나 상속 관계를 활용하지 못하는 서비스 로직들

그러나 지금 어플리케이션 서비스 로직엔 앞서 설명한 문제를 일으켰던 조건문 외에도 상속 관계의 이점을 사용하지 못하는 코드들이 존재했습니다.

public class FileService {    
    
    // 파일 정보를 수정하는 메서드
    public void updateFile(File file, FileRequest reqeust){
        if (file instanceOf ImageFIle imageFile) {
            imageFile.update(request.name(),
                             request.extension(),
                             request.resolution(),
                             request.weight(),
                             request.height());
        }
        if (file instanceOf DocumentFile documentFile) {
            documentFile.update(request.name(), request.extension());
        } 
    }
}

 

instanceOf 를 이용해 해당 파일이 이미지 파일인지, 문서 파일인지 구분한 후 파일 타입별로 알맞은 함수를 호출하는 조건문입니다.

이러한 instanceOf를 활용하는 로직이 문제가 되는 이유는 많은 분들이 이미 아실거라고 생각합니다.

  • 객체지향 원칙을 정면으로 위배하는 코드 (OCP, SRP 등)
  • 외부 객체가 자식 객체들의 구현을 알고 있음
  • 또한 앞서 문제가 됐던 생성 로직에선, 확장자를 이용해 생성할 파일 타입을 구분했는데 여기선 파일 객체 타입으로 구분.
    즉 같은 역할을 하는 코드가 여기저기 분리되어 다른 방식으로 구현되어 있음 등등

사실 구글에 “instanceOf 사용을 지양하라” 라는 키워드로 검색을 해보면 다양한 블로그 글들을 볼 수 있습니다.
그리고 그러한 블로그 글들은 모두 위와 비슷한 이유를 근거로 대면서 객체지향적인 프로그래밍을 위해 instanceOf 사용을 지양하고 다형성을 사용하라고 이야기 합니다.


다형성을 사용해보려했더니.. 어?

instanceOf 대신 다형성을 활용하기 위해 수정 로직을 다음과 같이 수정해보겠습니다.

// 파일 정보를 수정하는 메서드
public void updateFile(File file, FileRequest reqeust){
    file.update(request.name(), request.extension(), ???); //문제 발생
}

 

“어 근데 다형성을 사용할 수가 없는데?”

 

생각해보면 파일 타입 별로 응답 객체 생성과 수정 메서드에 들어가는 값이 다르다는 것을 알 수 있습니다.

이미지 파일은 문서 파일과 다르게, 수정 시에 가로, 세로, 해상도 값을 필요로 합니다.

 

이러한 상황에서 다형성을 활용하려면 다음과 같이 부모 클래스에서
들어올 수 있는 모든 값을 파라미터로 하는 update() 추상 메서드를 작성해야 합니다.

public class File {

    private Long id;
    private String name;
    private FileExtension extension;
    
    // 추상 메서드 사용
    public abstract void update(String name, 
                                FileExtension extension, 
                                double resolution, 
                                int width, 
                                int height) {}
}

public class DocumentFile extends File {

    @Override // 사용하지도 않을 width, heidhgt를 파라미터로 받는 문제
    public void update(String name, FileExtension extension, double resolution, int width, int height){
        this.name = name;
        this.extension = extension;
    }
}

public class ImageFile extends File {
    private double resolution;
    private int weight;
    private int height;
    
    @Override
    public void update(String name, FileExtension extension, double resolution, int width, int height){
        this.name = name;
        this.extension = extension;
        this.resolution = resolution;
        this.weight = width;
        this.height = height;
    }
}

 

그러나 이러한 로직은 향후 다양한 문제를 발생시킬 수 있습니다.

  1. 부모 클래스가 자식 클래스의 구현에 의존합니다. 새로운 변수를 가지는 파일 타입이 추가된다면 부모 클래스의 update 메서드와, 이를 구현한 자식 클래스의 메서드들도 모두 찾아가서 바꿔야만 합니다.
  2. 사용자는 인자로 들어간 값들로 파일 내부 정보를 바꿀 것을 예상하는데, Document 파일 구현을 들어가봐야지만 인자로 받은 값을 무시한다는 사실을 알 수 있습니다. 메서드 이름만 보고는 사용자가 어떻게 동작할지 예측할 수 없는 상황이 오게 됩니다.

결국 해답은, 서브타입 별로 update 함수를 구현한 후, 서비스 코드에서 타입별 캐스팅을 한 후 사용해야 합니다. 이 말은 즉 서비스에서 instanceOf를 완전히 제거하기는 것이 불가능하다는 것입니다.

사실 파일 도메인 내부에 isDocumentFile()과 같이 파일 타입을 체크하는 함수를 만듦으로서 표면적으로 instnaceOf를 제거하는 것은 가능합니다.
그러나 이는 파일 타입 조회를 instanceOf로 하냐, 내부 속성을 이용하냐의 차이일 뿐, 서비스에서 파일의 서브타입과 구현을 알아야 한다는 instanceOf의 단점을 해결할 수 없습니다.


다형성은 만능 해결책이 될 수 없다.

앞서 이야기한 대부분의 블로그 글에서 instanceOf 에 대해 다루는 이야기를 보면,
코드를 보다가 instnaceOf를 보자마자 속이 뒤집어지고, 알 수 없는 무언가가 끓어오르며, 당장 저 코드를 작성한 놈을 잡아다 멍석에 말아 두들겨 패고 소금을 뿌려 쫓아낸 다음, 다형성을 사용하라는 식으로 이야기합니다. (사실 그 정도는 아니다)

 

그런데 instanceOf를 무슨일이 있어도 사용하면 안되는 걸까요?

제임스 고슬링이 바보도 아니고, 의미 없는 기능을 우리를 괴롭히려고 만들었을리가 없습니다.

 

우리 요구사항을 충족하기 위해선 instanceOf나 그와 같은 역할을 하는 로직이 반드시 서비스에 존재해야 합니다.

그러나 instanceOf를 사용을 지양해야 하는 이유 역시 앞서 설명했듯이 명확합니다.

 

잠깐.. 그럼 애초에 이 상황에서 상속을 쓰는게 맞는 걸까요?

 

“instanceOf를 반드시 사용해야 하는 상황이 온다면, 그건 사실 상속을 활용하면 안되는 상황에서 상속을 사용중이라는 신호로 봐야하는 것 아닐까?” 라는 생각이 들기 시작했습니다.

 


우리는 상속 관계 매핑을 지양해야 하는가?

 

상속관계 매핑을 지양해야 하는가? - 인프런 | 커뮤니티 질문&답변

누구나 함께하는 인프런 커뮤니티. 모르면 묻고, 해답을 찾아보세요.

www.inflearn.com

 

검색을 해보던 중 JPA 대가 영한 선생님이 인프런 질문에 답변하신 내용을 찾아 가져왔습니다.

  • 객체의 상속관계를 활용할 것인가? (객체)
  • 아니면 객체 내부에 타입을 두고 해당 타입으로 구분할 것인가 (자료구조)

의 문제로 보라는 힌트를 받았습니다. 클린 코드 6장의 내용을 조금 더 살펴보겠습니다.

 

객체는 동작을 공개하고 자료를 숨긴다. 그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기 쉬운 반면, 기존 객체에 새 동작을 추가하기는 어렵다.

자료 구조는 별다른 동작 없이 자료를 노출한다. 그래서 기존 자료 구조에 새 동작을 추가하기는 쉬우나, 기존 함수에 새 자료 구조를 추가하기는 어렵다.

 

아무래도 고민하던 내용과 비슷한 것 같습니다.

그렇다면“우리 프로젝트와 같은 상황에서 상속 관계 매핑을 사용하는것이 맞는가?” 를 고민해봤을때

  • 객체의 상속관계(다형성)을 이용할 것인가
  • 객체 내부에 타입을 두고 구분할 것인가 (실질적 instanceOf 사용)

를 결정해야하고 전자는 엔티티를 객체, 후자는 자료구조로 보는 관점입니다.

그리고 우리는 다형성을 사용할 수 없는 상황이므로 후자를 선택해야만 합니다.


JPA 엔티티를 자료구조로 본다? 그럼 DDD는?

// 디미터 법칙을 위반하는 것처럼 보이는 코드
final String outputDir = ctxt.options.scratchDir.absolutePath;

클린 코드에선 자료구조는 해당 메서드 내부의 모든 내용을 자유롭게 접근할 수 있어야 하며 비즈니스 규칙 메서드를 추가해선 안된다고 이야기합니다. 디미터 법칙과 이를 어기는 "기차 충돌" 코드도 객체라면 문제가 있지만, 자료구조에선 용인해도 된다고 이야기하기도 합니다.

 

이러한 엔티티를 자료구조로 보는 관점은 DDD의 전술적 패턴과 부유한 도메인 모델을 전적으로 부정하는 일이 아닌가 하는 생각이 듭니다.

 

그런데 DDD는 항상 적용해야 하는 절대적인 개념인가라는 물음에 대한 대답은 “아니오” 라고 단언할 수 있습니다.

도메인 주도 개발 시작하기에서도 이런 이야기를 합니다.

비즈니스 로직이 간단한 CRUD를 벗어나지 않으며, 다른 도메인과 연관관계가 복잡하지 않다면, 때로는 트랜잭션 스크립트 패턴, 고전적인 컨트롤러 서비스 도메인 패턴을 사용하는 것이 유지보수에 더 유리할 수 있다.

 

우리의 파일 도메인은 어떨까요? 제 생각에는

비즈니스 로직은 CRUD 로직에 presigned-url을 발급하는 로직 정도만 추가되어 있으며,
다른 도메인과 연결될 가능성도 매우 적은 간단한 도메인입니다.

 

이런 경우, DDD에 매몰되어 객체로만 보기보단, 조금 더 유연한 사고가 필요할 때입니다.


 

이제 알잘딱하게 리팩토링해보자

1. 확장자를 이용해서 파일 타입을 알 수 있도록 하기

@Getter
@RequiredArgsConstructor
public enum FileExtension {
    JPG("jpg", IMAGE),
    XML("xml", DOCUMENT),
    PDF("pdf", DOCUMENT),
    TXT("txt", DOCUMENT);

    private final String value;
    private final FileType fileType;
}

public enum FileType {
    DOCUMENT, IMAGE;
}

우선 위와 같이 확장자를 이용해서 파일 타입을 알 수 있도록 하기 위해서
확장자 enum 내부에 파일 타입을 분류할 수 있는 enum 항목을 추가했습니다.

 

사실 파일 타입은 확장자만으로 결정되며, 같은 확장자가 여러 파일 타입을 가질 확률도 매우 적어 이렇게 변경하기로 결정했습니다.

 

이제 글의 맨처음 문제가 발생했던 파일 생성 전 구분 로직, instanceOf를 사용하는 로직들을 다음과 같이 바꿀 수 있습니다.

public class FileService {
    
    // == 변경 전 ==
    private File createFile(FileCreateRequest request) {
        FileExtension extension = request.extension();
        
        if(isFileTypeDocument(extension)){
            return createDocumentFile(request);
        } else {
            return createImageFile(request);
        }
    }
    // 문제의 로직
    private boolean isFileTypeDocument(FileExtension extension) {
        return extension == PDF || extension == XML;
    }
    
    // == 변경 후 ==  
    public File createFile(FileCreateRequest request) {
        FileExtension extension = request.extension();
        
        if (extension.getFileType() == IMAGE) {
            return ImageFile.create(
                             fileName, 
                             extension, 
                             resolution, 
                             width, 
                             height);
        } 
        else if (extension.getFileType() == DOCUMENT) {
            return DocumentFile.create(fileName, extension);
        } 
        else {
            throw new Exception("Invalid FileType");
        }
    }

    //변경 전
    public void updateFile(File file, FileRequest reqeust){
        if (file instanceOf ImageFIle imageFile) {
            imageFile.update(request.name(),
                             request.extension(),
                             request.resolution(),
                             request.weight(),
                             request.height());
        }
        if (file instanceOf DocumentFile documentFile) {
            documentFile.update(request.name(), request.extension());
        } 
    }
    
    // 변경 후
    public void updateFile(File file, FileRequest reqeust){
        FileExtension extension
        
        if (extension.getFileType() == IMAGE) {
            ImageFile imageFile = (ImageFile) file;
            imageFile.update(request.name(),
                             request.extension(),
                             request.resolution(),
                             request.weight(),
                             request.height());
        }
        if (extension.getFileType() == DOCUMENT) {
            DocumentFile documentFile = (DocumentFile) file;
            documentFile.update(request.name(), request.extension());
        } 
    }
}

이제 새로운 파일 확장자가 추가되더라도, 파일 서비스는 변경할 필요 없이 FileExtension enum만 추가하면 됩니다.

 

2. 변경에 취약한 부분을 몰아넣기

사실 위와 같은 코드가 FileService의 비즈니스 로직의 전부는 아닙니다.

Presiend-URL을 받아오는 로직도 존재해야하고 파일 조회, 삭제와 같은 로직도 FileService 내부에 존재합니다.

 

사실 이 글의 예시 코드는 원활한 설명을 위해, 실제 코드를 참고하여 새롭게 만들어 온 것에 가깝습니다.

실제 코드는 조금 더 다양한 역할을 수행하고 있었습니다. 그런데 만약 새로운 파일 타입이 추가된다면..?

 

위의 생성, 수정 로직의 if문은 추가된 파일 타입에 맞춰 변경되어야 합니다.

그리고 하나씩 수정하는 과정에서 놓치는 부분이 있다면 역시 문제가 됩니다.

 

이걸 해결하는 방법은 여러가지가 있겠지만, 변경에 취약한 부분만 따로 몰아넣는 것도 좋은 해결책입니다.

그리고 사실 조건에 따라 알맞은 객체를 생성하여 반환하는 좋은 방법을 모두 알고 있습니다.

바로 팩토리 클래스를 활용하는 것입니다.

public class FileFactory {

    public File createFile(String fileName, FileExtension extension, double resolution, int width, int height) {
        
        if (extension.getFileType() == FileType.IMAGE) {
            return ImageFile.create(
                             fileName, 
                             extension, 
                             resolution, 
                             width, 
                             height);
        } 
        else if (extension.getFileType() == FileType.DOCUMENT) {
            return DocumentFile.create(fileName, extension);
        } 
        else {
            throw new Exception("Invalid FileType");
        }
    }
}

 

렇게 팩토리 클래스를 도메인 서비스 처럼 만들어 관리한다면,
파일 타입 추가에 취약한 코드들을 어플리케이션 서비스에서 분리할 수 있습니다.

 

3. 상속 관계 매핑 해제하기

그리고 이렇게 파일 도메인 내부의 Extension을 이용해 파일 타입을 구분한다면 더 이상 상속을 사용해야만 하는 이유는 없어집니다.

어차피 단일 테이블을 사용해 데이터를 저장하니, 다음과 같이 합성을 이용하도록 변경해볼 수도 있겠습니다.

public class File {

    private Long id;
    private String name;
    private FileExtension extension;
    private ImageData imageData;
    ...
    
    public void updateDocumentFile(String name, FileExtension extension){
        ....
    }
    
    public void updateImageFile(String name, FileExtension extension, ImageData imageData){
        ....
    }
}

public class ImageProperties {
    
    private double resolution;
    private int width;
    private int height;
}

 

그러나 여기까지 진행하진 않았습니다. 그 이유는

  1. 상속 관계의 엔티티 정의가 비즈니스 규칙을 명료하게 제공한다.
    • 파일 타입에는 이미지 파일과 문서 파일이 있고.. 이미지 파일은 어떤 정보를 추가로 가지고… 와 같은 정보를 엔티티 정의를 본다면 명확하게 파악할 수 있습니다.
  2. 타입 캐스팅을 통해 혹시 모를 엔티티의 잘못된 사용을 예방할 수 있다.
    • 문서 파일에서 해상도를 호출하려 하면 문제가 발생해야 합니다. 상속을 통해 문서 파일과 이미지 파일을 분리하면, 위와 같은 상황을 막을 수 있습니다.
  3. 잘 사용하고 있던 DB 테이블 구조 변경에 대한 부담
    • 상속을 사용하지 않는다면 DB 테이블에서 더이상 dtype 컬럼을 사용할 필요가 없습니다. 그러나 “이미 잘 사용하고 있던 DB 테이블까지 바꾸면서 까지 고쳐야할 문제인가?” 는 아니라고 생각했습니다.

 


 

결론 & 후기

상속관계 매핑을 사용하던 중 instancOf를 사용할 수 밖에 없는 상황이 생겼다면 “엔티티 내부에 타입(Enum FileType)을 두고, 엔티티를 자료구조로 사용해 외부에서 타입을 읽고 사용하는 방법도 고려해봐야 한다.” 라는 결론입니다.

 

간단한 리팩토링이었지만 그 과정에서 “instanceOf 사용을 지양해야 하는가?”와 같이 블로그만 읽고 대충 알던 지식들을 제대로 공부해보고 상황에 맞춰 적용하기 위해 고민해보는 좋은 경험을 얻었습니다.
또한 "은탄환은 없다"와 가장 좋은 설계란 내 상황에 가장 알맞은 설계이다. 라는 자주 잊는 진리를 다시 한 번 깨닫게 됐습니다.

 

사실 어그로를 잔뜩 끈 서론에 비해 결론은 보잘것 없으며, 결론에 비해 결과물은 더 애매한 리팩토링으로 끝났습니다.

상속 구조는 아직도 사용 중이며, 서비스 코드와 팩토리 클래스엔 중복된 코드가 존재합니다.

 

리팩토링을 하던 도중 고민하던 저에게 A씨가 WET 원칙과 DRY 원칙을 반대하는 사람들의 의견에 대해 이야기해줬습니다. 

“관리가 가능한 중복 코드는 너무 두려워 할 필요는 없다” 라는 이야기로 들렸습니다.

https://news.hada.io/topic?id=15102

 

코드를 성급하게 DRY하지 마세요 | GeekNews

코드 중복 제거를 너무 일찍 하지 말아야 하는 이유DRY(Don't Repeat Yourself) 원칙을 너무 엄격하게 적용하면 "Premature" 추상화를 유발하여 미래의 변경이 더 복잡해질 수 있음중복이 진짜로 불필요한

news.hada.io

 

 

비슷한 의미로 영한 선생님은 "어느날 고민 많은 주니어 개발자가 찾아왔다" 라는 세미나에서 
“추상화도 비용이다”라는 이야기를 하셨습니다.

 

객체지향 원칙은 중요합니다.

하지만 변경의 범위를 한정하고, 변경에 취약한 코드를 한쪽으로 몰아넣는 선에서 중복 코드와 의존관계를 잘 관리할 수 있다면
성급한 추상화보다 좋은 해결책이 될 수 있습니다.

 

완벽한 객체지향 코드를 만드는 추상화의 비용이 때로는 중복 코드와 절차지향 코드를 사용하는 것보다 클 때도 있다는 사실을 잊지 않아야겠습니다.

 

분별있는 프로그래머는 모든 것이 객체라는 생각이 미신임을 잘 안다.
때로는 단순한 자료구조와 절차적인 코드가 가장 적합한 상황도 있다. 
-클린 코드-

 

 

 

잘못된 내용을 보시거나 다른 견해가 있다면 언제든 댓글 부탁드립니다.

  • 여담으로 저 리팩토링 이후 약 한 달 뒤 확장자를 추가해야 하는 상황이 생겼습니다.
    enum 하나 추가하는 것으로 다른 코드 하나도 안건드리고 깔끔하게 확장자 추가에도 성공했는데 기분이 상당히 좋았습니다.

 

서론 - 불안 세대를 읽고 나서

회고록에서 갑자기 왜 책이 튀어나왔냐 싶겠지만, 24년에 마지막으로 읽은 책이다.

이 책을 읽다가 몇가지 생각을 하게 되었는데 이게 24년 회고록과 이어지게 되었다.

흥미롭게 읽어서 읽어볼 생각이 있으면 추천한다.


아동기 대재편에 대하여

선사시대 움집 마을부터, 2000년대 대도시까지 군집 생활을 하는 인간의 생활 모습은 많이 달라졌지만

아동기(만 12세 미만)를 보내는 아이들의 모습은 항상 같았다.

아동기엔 깨어있는 시간의 대부분을 또래와 “놀이”를 하면서 보낸다.

놀이는 훌륭한 교육 체계이다

  • 아이들의 놀이는 다양하고 복잡한 규칙이 존재한다. 때론 스스로 규칙을 만들기도 한다.
  • 합의한 규칙을 어기면서 이기려고 하면 놀이에서 배제당한다. (더 이상 친구들이 끼워주지 않는다)
  • 놀이 과정에서 서로 다툼도 발생하고 이를 중재하며 갈등 해결 방법을 배운다.
  • 적당한 위험과 스릴을 감수하며 자신의 신체를 조절하는 방법에 대해 배운다

아이들은 현실세계에서 "적당한 위험"을 가진 놀이를 하며 성장한다.

그러나 2010년대 들어 스마트폰의 도입과 함께 아이들이 현실세계에서 놀이를 하는 시간이 줄어들었다.

 

현실 세계에서의 과잉 보호와 인터넷 세계에서의 방임

아이들이 노는 장소를 설계할 때 목표는
“아이들의 안전을 최대한 보장하는 것이 아니라 필요한 만큼만 보장하는 것”이 되어야 한다.

 

왼쪽이 내가 어린시절 보던 야생(?) 놀이터이고, 오른쪽은 최근 아파트 단지에서 자주 보이는 놀이터이다.

 

 

어느 순간부터 놀이터에서 아이들이 잘 보이지 않는다. 정확하게는 10~12세(초 5 ~ 중1)의 아이들.

 

내가 기억하는 초등학교 놀이터는 왼쪽 그림과 같았다.

하지만 어느 순간부터 아파트가 많이 들어서기 시작했고, 아파트 단지에 생기는 놀이터는 오른쪽 그림과 같이 지어졌다.

 

오른쪽 그림에서 보이는 요즘 놀이터는 초등학교 고학년을 넘어가는 아이들에게는 시시하게 느껴진다.

이 또래 아이들은 이제 놀이터보다는 PC방과 오버워치 팀 보이스에서 더 자주 발견된다.

  • 부모들의 과보호
    • 예전엔 낮에 아이들이 어디서 뭘 하는지 부모들이 신경을 많이 쓰지 않았다.
    • 뉴스와 미디어의 발달로 부모들의 안전에 대한 걱정, 염려가 증가했고, 아동기 아이를 혼자 두는 경우가 매우 줄었다.
    • 또한 아이들이 적당한 위험을 감수하며 놀만한 시설들이 사라졌다.
      아이가 놀다가 다치면 책임을 물기 시작하는데 누가 굳이 위험한 놀이터를 지으려 하겠는가
  • SNS와 인터넷, 온라인 게임의 발달
    • 굳이 놀이터에 가서 놀아야 할 이유를 찾지 못한다. 게임에만 들어가면 모든게 준비되어 있으니까
    • 부모들의 입장에서도 아이가 밖에서 위험한 놀이를 하는 것 보다, 집에서 게임을 하는 것이 더 안전해보일 것이다.

실제로 아동기, 청소년기 아이들이 신체적,물리적 부상을 겪어 병원에 가는 빈도는 꾸준히 줄고 있다.

근데 이게 과연 좋은 일일까?

 

온실속 화초는 사실 병들어있다

시대별 청소년의 자살률과 불안장애율 그래프이다. 2010~2014년을 기점으로 급상승하는 걸 볼 수 있다.

 

아동기에 “위험”을 감수하는 경험을 많이 하지 못한 사람은 나중에 우울증, 불안 장애를 겪을 확률이 증가한다.

 

전세계적으로 스마트폰이 대부분의 아이들에게 보급되기 시작한 시점(2010-2014년 무렵)부터
청소년기 우울증, 불안 장애 환자 수는 기하급수적으로 증가했다.

 

어린 시절에 작음 위험에 노출이 많이 되어야 나중에 훨씬 큰 위험에 대처할 수 있는 어른이 된다.

반면 어릴때 과잉 보호를 받은 아이는 위험 앞에서 불안과 두려움에 사로잡힌 어른이 되기도 한다.

 

[불안 세대] 에서는 이를 "놀이 기반 아동기"에서 "스마트폰 기반 아동기"로의 아동기 대재편이라고 이야기한다.

그리고 이러한 변화가 최근 청소년 우울증, 불안장애의 급격한 증가를 일으켰다고 주장한다.

 


나의 어린 시절은 어땠을까

나는 스마트폰을 초등학교 5학년(만 10세) 부터 사용하기 시작했다.

그러나 운이 좋게도, 내 아동기의 대부분은 놀이 기반이었다.

 

놀이 기반 아동기의 마지막 탑승자

아마 우리 세대가 놀이 기반 아동기에서 스마트폰 기반 아동기로 전환이 진행되던 세대였던 것 같다.

 

초등학교 저학년때까지는 놀이터와, 운동장, 친구집을 돌아다니며 놀다가
초등학교 고학년, 중학생때부터 스마트폰이 생기기 시작했던 세대.

 

운이 좋은건지 안 좋은건지, 내가 자란 하남은 경기도 내에서도 교육열이 높은 지역이 아니었다.

주변에 태권도나 피아노가 아닌 학원을 다니는 친구들은 매우 드물었고,

주말만 되면 아침을 먹자마자 나가 동네 친구들을 모으고 해가 질때까지 동네를 뛰어다니면서 놀았다.

여러분도 '지탈'을 아시나요?

 

초등학교 5학년때 스마트폰을 처음 받았지만 놀이의 방법이 약간 달라졌을 뿐, 여전히 또래 친구들과 직접 만나 놀았다.

(친구들과 모여서 핫스팟 틀고 모바일 마인크래프트 멀티 하다가 데이터 폭탄 맞고 엄마한테 혼났었다.)

 

이러한 환경이 지금 생각해보면, 놀이 기반 아동기를 경험할 수 있게 해준 좋은 환경이었을지도 모르겠다.

 

정글짐 꼭대기를 올라가지 못했던 아이

나의 학창시절을 돌아보면 매우 모범생이었다고 자부할 수 있다.(진짜다)

선생님이 하지 말라던건 어지간해서 안 했던 말 잘듣던 아이였다.

 

하지만 난 이게 내 천성이 착해서가 아닌 “겁이 많아서” 라고 생각한다.

 

초등학생때 학교 놀이터엔 정글짐이 하나 있었다.

난 저 정글짐 꼭대기를 올라가보질 못했다.

 

꼭대기에서 바로 아래칸까지 올라가고나면 다리가 후들거리고, 떨어질까 무서워 더 올라가지 못하고 내려왔다.

주변 친구들은 꼭대기에서 놀고 있는데 나만 아래서 부러운 눈으로 구경하곤 했다.

 

높은 곳만 무서워하는게 아니라 대체로 겁이 많은 아이였다.

병원에서 주사 맞을일만 있으면 울고 불고 난리가 났었고, 공포영화는 포스터만 봐도 무서워서 눈을 돌렸다.

친구들과 경찰과 도둑을 할때면 누군가한테 쫒기는 상황이 무서워서 항상 경찰을 골랐다.

 

이렇게 겁이 많은 탓에, 나는 어린 시절에 모험을 겪는 걸 별로 좋아하지 않았다.

(유치원때 놀이터에서 그네 주변을 지나가다가 그네를 타고 있던 친구와 부딪힌 이후로, 그네 근처도 못갔다.

그래서 처음 그네를 탔던게 초등학교 4학년이었다. 학교 운동장에 널린게 그네였는데 말이다.)

 


불안함이라는 흉터

다행히도 겁쟁이 소년은 청소년기를 거치고 나서 씩씩한 어른이 되었다.

바늘이 무서워 울던 아이가 이젠 봉사활동이 귀찮아서 제발로 헌혈을 하러 간다.

(그리고 씩씩한 어른이 된 댓가로 현역으로 군대에 갔다.)

 

 

그런데 어릴때 위험을 감수한 경험이 많이 없어서일까?

나이가 들고 겁은 사라졌지만, 가끔씩 찾아오는 불안함이 나를 힘들게 했다.

 

특히 내가 경험해보지 못한 상황이 갑작스럽게 닥쳤을때 내 안의 불안이가 폭주했다.

 

군대와 불안감

군대는 내가 경험해보지 못한 상황들의 연속이었다.

훈련소 두 달, 그리고 자대에 가서 두 달 총합 4달 정도, 잠을 제대로 못 잤던 것 같다.

 

군대는 내가 잘 알지도 못하는 규칙들이 모든 곳에 존재했으며,

그걸 어기는 순간 소리를 지르고 화를 내는 사람들이 24시간 나랑 붙어있었다.

 

화장실을 가는 것도, 물을 마시는 것도 눈치를 봐야했고, 주변엔 친한 사람은 커녕 처음 보는 빡빡이들만 가득했다.

그래서 이등병, 일병 초 정도는 24시간 내내 조교와 선임, 간부들의 눈치를 보느라 너무 피곤했다.

 

근데 이러한 불안감이 항상 나쁘게만 작용하지는 않았다.

 

겁쟁이와 에이스는 한끝차이

24시간 눈치를 보며 사는게 나한테는 피곤했지만 군생활엔 도움이 많이 됐다.

선생님께 혼이 날까 두려워 모범생처럼 살았던 아이는, 군대에선 선임에게 혼이 날까 두려워 에이스가 되었다.

 

공군에서도 작은 부대였던 우리 부대는 흔히 말하는 '가라부대'였다.

애초에 할 일이 많지도 않았고, 대부분 사람들이 규정 같은것을 지키지 않고 대충대충 하는 분위기였다.

 

하지만 나는 규칙을 어기면 돌아올 상황이 두려워 모든 규칙을 지키며 fm대로 살았다.

내 관물함과 자리는 전역 전날까지 꺠끗했고, 우리 부대 병사 80프로 이상이 쓰던 투폰은 시도할 엄두도 안냈다.

 

“이게 옳다고 생각해서”가 아니라 규칙을 어기다가 걸리면 혼날까봐 했던거여서,
주변 동기나 후임들은 규칙을 어겨도 놔뒀다. (걸려도 내가 혼나는거 아니니까)

 

근데 전역할때쯤 되보니까, 본인은 fm대로 살면서, 후임한텐 너그러운 사람이 되어 있었다.

선임들과 간부들도 나를 좋게 봐줬고, 중요한 일을 해야 할때 나를 찾는 사람들이 생겼다.

 

그러다 보니 나름 군대에서 병사로 할 수 있는 감투를 많이 써봤던 것 같다.

(공군 출신은 알겠지만, 새내기 생활관장, 병사 자율위원회 등등 )

 

군대는 감옥이었나 울타리였나

 

나는 내가 군대에서 나름 열심히 살았다고 생각했다.

열악한 사지방 환경에서 공부도 하고 프로젝트도 완성했다.

 

그 안에선 무얼 하든 모래주머니를 달고 달리는 것 같았다. 

밖에만 나가면 모래주머니를 벗어 던지고 훨씬 빨리 뛸 수 있을거라 생각했는데

작년 5월 말 전역을 하고 한 달 동안 아무것도 안했다. 정말 말 그대로 아무것도.


모래주머니와 나를 감싸던 철조망이 사라졌지만, 정작 어디로 달려가야 할 지 몰라 가만히 서있었다.

 

군대에선 도전이란 걸 할 필요가 없었다. 아침에 일어나서부터 자기 전까지 모든 시간을 통제하는 곳이다.

전역을 할때쯤, 오히려 매일 일상이 똑같은 군대에서 편안함을 느끼기 시작했다.

 

그러다 전역을 하고 나니 길을 잃어버린 느낌이었다.

 

지금 당장 무엇을 해야할까 고민만 하다가 한 달이 지나갔다.

 

모래주머니가 아니라 갑옷이었나

감옥이라고 생각했는데 울타리였나.


전역, 이제는 더 이상 물러날 곳이 없다

 

6월 말이 되어서야 정신을 차리고 일단 아르바이트라도 구해야겠다 싶어 알바천국, 알바몬을 뒤졌다.

그리고 집 앞 메가박스에서 아르바이트생 모집 공고가 올라왔다.

 

전부터 영화를 좋아했고, 영화관 알바를 한 번쯤은 해보고 싶어서 바로 지원서를 쓰기 시작했다.

그리고 그날 저녁 오랜만에 다시 불안함이 드리웠다. 1년 반 전 이등병 시절 느꼈던 그 불안감.

첫 만남은 너무 어려워

지원서 자체를 쓰는 것은 그렇게 어렵지 않았다. 하지만 지원서를 제출한 날 밤부터, 불안한 생각이 들기 시작했다.

  • 영화관 아르바이트 경력이 아예 없는 나를 뽑아줄까?
  • 영화관 아르바이트는 면접이 빡세다던데 얼마나 준비해야하지?
  • 막상 갔는데 분위기가 안좋은 곳이면 어떡하지?
  • 학기랑 아르바이트를 병행할 수 있을까?

지원서를 쓰고 면접을 보러 가기 전까지 불안하고 초조한 마음이 계속 들었다.

인터넷에서 영화관 면접 후기들을 찾아보고, 계속해서 시뮬레이션을 하며 준비했다

 

면접 보러 가기 전 날엔 새벽 4시까지 불안해서 잠을 이루질 못했다.

면접 당일엔 원래 보기로 한 시간보다 20분 일찍 가서 기다렸다. (집에서 걸어서 10분 거리였는데 말이다.)

 

물론 이런 불안함이 앞서 말했듯 항상 나쁜 결과로 이어지는 것은 아니었다.

 

불안함 때문에 오히려 철저히 준비를 해갔고 7명 중에서 1명 뽑는 면접에 당당히 합격했다.

 

나를 뽑아주신 매니저님이 나중에 이야기 해주셨는데

미리 준비한 보건증, 면접전에 20분 일찍와서 기다린 점 등을 좋게 보고 나를 뽑자고 하셨다고 한다.

 

작년 유일하게 후회하는 일

 

메가박스 알바를 하면서, 동기간에 처음으로 웹 개발 외주를 진행했었다.

외주의 양은 그렇게 많지 않았고, 개강 전에 끝낼 수 있을거라고 생각했다.

 

그리고 군대에서 진행했던 프로젝트와, 이 외주를 포트폴리오로 CEOS에 지원하려고 했었다.

그리고 CEOS 지원 기간이 되니 불안함이 다시 드리웠다.

 

그리고 이번엔 지원서를 쓰지 못했다.

 

사실 여러 이유가 있었다.

  • 외주가 예상 보다 마지막에 너무 일이 많아지고 바빠졌다. 이게 CEOS 접수 기간과 정확하게 겹쳤다.
  • 또 학기 중에 GDSC 멘토를 하기로 결정했고, CEOS와 GDSC 멘토를 병행할 수 있을지 이 됐다.

그런데 결국 가장 큰 이유는 두려움이었던 것 같다.

  • 지원했다가 떨어지면 어떡하지
  • 제대로 면접을 준비할 시간이 없는데, 면접에서 쪽팔림만 당하고 오는건 아닐까?
  • 지뎃씨 멘토와 CEOS 둘 다 잘 못하는건 아닐까?

 

주변에서 쎄오스 지원했냐고 물어봤을때 제일 먼저 대답한 이유는 외주가 바빴다 였지만,

사실 이러한 두려움과 불안감에 지원을 못한게 맞는 것 같다.

 

두려움과 후회

그리고 학기를 시작하고 나서 CEOS 신청을 못한걸 굉장히 후회하게 됐다.

생각보다 학기 중에 시간 여유가 꽤 있었고, 오히려 남는 시간에 뭘 해야할지 다시 길을 잃었다.

 

나는 왜 시도도 안 해보고 두려워했을까?

연합 동아리가 무슨 정시 원서도 아니고 떨어진다고 다른 리스크가 있었나?

이거 떨어진다고 주변 사람들이 나를 벌레보듯이 볼까?

 

전혀 아닌데 말이다.

도전해본다고 해서 손해 보는게 단 한가지도 없었는데, 오로지 두려움 때문에 시작을 못했다는 사실이 후회가 됐다.

 

학기 중 남는 시간에 유의미한 개발 공부를 하지 못해서 더 아쉬웠을지도 모르겠다.

 

낯선 상황을 겪을때 스트레스를 받고 불안한건 어쩔 수 없는 일이다.

그런데 스트레스를 받는 것과 별개로 스트레스를 받는 상황이 두려워 회피하는 것은 다른 문제이다.

 

물론 지원했다고 무조건 붙는 건 아니지만, 작년 1년을 되돌아보며 유일하게 후회가 남는 점이다.


이제야 도전해보는 중

그래서 지난 2학기에 남는 시간엔 전공 공부에만 집중했다.

운이 좋게도 학점은 나쁘지 않게 받았지만, 개발 실력은 2학기 동안 전혀 늘지 않았다.

 

사실 디프만 몰래 지원하고 광탈했습니다

 

그래서 사실 기말고사 기간에 모집 공고가 올라온 디프만에 지원했다.

 

내가 디프만을 뚫고 들어갈 정도의 실력을 가지고 있는 것도 아니었고,

지원서를 쓰는 기간이 기말고사 기간과 딱 겹쳐서 지원서를 쓸 시간도 많이 없었다.

 

그렇지만 이번엔 지원서도 안 넣어보고 후회를 하고 싶진 않았고,

GPT의 도움을 조금 받아 지원서를 작성해 제출했다.

 

사실 제대로 된 기업이나, 개발 관련 동아리에 지원서나 이력서를 쓴 경험이 처음이었다.

결국 서류에서 광탈하긴 했지만 지원서를 쓰면서 나의 부족한 점을 찾을 수 있었다.

 

면접까지 갔다면 더 도움이 많이 되었겠지만, 이것만으로도 큰 도움이 되었다고 생각한다.

가자 회사로

그리고 종강 이후에 회사에 가기로 결정했다.

내 주변 사람들은 모두 아는 A씨가 작년 2학기 쯤 부터 회사 인턴을 같이 할 사람을 구하고 있었다.

 

조건은 1년 휴학 및 계약직 입사.

1년 휴학이라는 조건이 부담스러워 처음엔 거절했지만, 종강 이후에 들어가기로 마음을 바꿨다.

 

디프만 지원서를 쓰다보니 나의 부족한 부분이 뼈저리게 느껴졌고,

현업 1년 경험이 큰 도움이 될 거라고 생각해서 놓치고 싶지 않았다.

 

1년이라는 시간은 짧으면서 긴 시간이다.

이 1년을 잘못하면 흔히 말하는 ‘물경력’ 이라는 무의미한 시간으로 보낼 가능성도 있다.

그리고 사실 내 실력이 회사에서 일하기엔 턱없이 부족하다는 생각도 든다.

 

하지만 더 이상 두려움 때문에 다가오는 기회를 놓칠 순 없다고 생각했다.

이젠 더 이상 군대처럼 내 시간과 일정을 누군가가 통제하지 않는다.

 

울타리가 사라졌으니 나 스스로 길을 찾아 달려나가야한다.

25년의 목표

어떤 길을 가던 위험성이 없는 길은 존재하지 않는다.

하지만 제자리에 서있다면 아무것도 바뀌지 않는다.

 

어릴때 내가 조금 더 겁이 없었더라면, 작음 위험을 감수해보는 경험을 해봤더라면,

지금의 내가 가진 두려움과 불안이 줄어들었을까?

 

나의 어린 시절을 되돌릴 순 없다.

늦었지만, 두려움을 이겨내고 위험을 감수해봐야한다.

 

30대, 40대가 된다면, 선택 한번 한번의 위험성이 더 커질테니까

그래서 25년은 1년 휴학과 회사 입사라는 도전을 하며 시작했다.

 

여기서 멈추지 않고 또 다양한 도전을 해볼 생각이다.

겁 많았던 아이는 이제서야 어른이 되는 중이다.

 

위험하고, 무섭지만 정글짐 꼭대기에서 올라가서 본 놀이터 풍경은 더 아름답지 않을까?

 

 

2편에서 튜링 머신과 튜링 완전한 프로그래밍 언어에 대해 설명했다.

이제 직접 튜링 완전한 프로그래밍 언어를 만들어보자.

 

교수님 습관으로 프로그래밍 언어 만들기

내가 만들 프로그래밍 언어는 사실 우리과 교수님의 말버릇이다.

이왕 만드는 거 재밌게 만드는게 좋으니까, 평소에 좋아하는 교수님의 말버릇을 주제로 만들어봤다.

(이런 글을 써도 되는지 모르겠지만, 혹시 문제가 되면 바로 지우겠습니다. 죄송합니다 교수님)

 

교수님의 평소 강의 말버릇이 조금 독특하신데,

영어 강의를 진행하실 때 명사 앞에 the 와 a를 거의 매번 같이 붙이신다.

 

그래서 듣다보면 디어,,, 디어,,,가 반복되는데, 어느새 우리 과 컴공 사람들은 대부분 아는 교수님의 시그니처가 됐다.

(다시 한 번 말하지만 교수님을 희화하하려는 것이 아닙니다... 저는 교수님 수업 정말 재밌게 잘 들었습니다. 교수님 사랑해요)

 

그리고 수업 중간 중간에 '이런건 앞으로 여러분들 인생에 아무짝에도 쓸모없는거죠' 라고 하면서 자주 설명을 해주신다.

근데 그렇게 말씀하셔놓고 설명도 자세하게 해주시고, 내용도 흥미로워서 재밌게 들었는데 이것도 넣어봤다.

 

언어의 구조는 2편에서 설명한 BrainFu**과 동일하게 만들었다.

포인터 증가
디이 포인터 감소
. 포인터가 가리키는 값 1 증가
, 포인터가 가리키는 값 1 감소
포인터에 저장된 값 ASCII 코드로 출력
어어 키보드로 값을 입력 받아 포인터에 저장
앞으로인생에있어서 반복문 시작
아무쓸모도없는거죠 반복문 종료(포인터 값이 0이 아니면 시작으로 이동)

 

재미를 위해서 코드의 시작과 끝에는 교수님의 시작과 끝 멘트인

'헬로에브리원투데이이즈' 와 ''오늘은여기까지하겠습니다' 를 넣어야 하도록 해봤다.

 

이제 이 언어로 HELLO WORLD를 출력해보자

헬로에브리원투데이이즈

디..... ..
앞으로인생에있어서
디..... .....
디..... .....
디..... .....
디..... .....
디이 디이 디이 디이,
아무쓸모도없는거죠

디.. 어
디, 어
디..... .어
어
디..... ....어

디...
앞으로인생에있어서
디..... .....
디이,
아무쓸모도없는거죠
디...어

디..... .....
앞으로인생에있어서
디..... ....
디..... ...
디..... ...
디..... ...
디..... ..
디이 디이 디이 디이 디이,
아무쓸모도없는거죠
디,,,어
디, 어
디..어
디,,,,어
디,,어

오늘은여기까지하겠습니다

컴파일러 동작 과정 알아보기

https://gobae.tistory.com/94

 

[컴파일러] 토크나이저, 렉서, 파서 (Tokenizer, Lexer, Parser)

컴파일러 [컴파일러] 컴파일러란? 컴파일러 컴파일러는 명령어 번역 프로그램이다. 컴파일러는 소스 코드 혹은 원시 코드를 목적 코드로 옮겨주는 역할을 한다. 쉽게 설명하면 여기서 소스 코드

gobae.tistory.com

 

 

아직 프로그래밍 언어나 컴파일러에 대한 수업을 들어보질 않아서, 네이버 블로그를 참고했다.

프로그래밍 언어가 기계화로 바뀌는 과정은 구문 분석, 최적화, 코드 생성, 링킹의 과정을 거친다고 한다.

 

그러나 우리는 실제 기계어로 변환해주는 컴파일러를 구현하지는 않을 거고, 여러 파일을 합쳐 실행파일을 만드는 기능도 필요없다.

(어차피못만든다)

최적화도 할 필요가 없다. 최적화가 필요하면 이런걸 쓰겠냐

 

그래서 구문 분석후 실행의 역할만 하면 된다.

그래서 컴파일러의 Tokenizer, Lexar, Parser를 이용한 구문 분석 과정을 조금 참고해서 구현해봤다.

 

Tokenizer은 코드를 최소한의 의미를 가지는 '토큰'으로 쪼개는 역할을 한다.

또한 이 과정에서 필요 없는 문자(공백, 주석) 등을 제거한다.

 

Lexar는 Tokenizer에 의해 쪼개진 토큰의 의미를 분석한다. (해당 토큰에 의미를 부여한다)

 

Parser는 의미가 부여된 토큰들을 계층 구조로 나타낸다.

 

해당 과정을 모두 거치고 나면 분석된 구문은 트리 형태로 정렬 된다.

이렇게 구문을 분석해 만든 트리를 AST(Abstract Syntax Tree)라고 한다.

 

Tokenizer 구현하기

class Compiler():

    def compile(self,filename):
        ...

    def tokenize(self, file):
        lines = file.readlines()
        tokens = []

        #split every lines by blank
        for line in lines:
            words = line.strip().split() #remove \n
            if words == []: continue #skip empty line

            for word in words:
                start = 0
                now = 0
                while now < len(word):
                    if word[now] == '디':
                        if not start == now:
                            tokens.append(word[start:now])
                        if now+1 < len(word) and word[now+1] == '이':
                            tokens.append(word[now:now+2])
                            now += 2
                            start = now
                        else:
                            tokens.append(word[now])
                            now += 1
                            start = now

                    elif word[now] == '어':
                        if not start == now:
                            tokens.append(word[start:now])
                        if now+1 < len(word) and word[now+1] == '어':
                            tokens.append(word[now:now+2])
                            now += 2
                            start = now
                        else:
                            tokens.append(word[now])
                            now += 1
                            start = now
                    elif word[now] == '앞' or word[now] == '아':
                        if not start == now:
                            tokens.append(word[start:now])
                        tokens.append(word[now:now+9])
                        now += 9
                        start = now
                    else:
                        now += 1

                if not start == now:
                    tokens.append(word[start:now])

        return tokens

 

우리 코드의 Token은 각각의 명령어들이다.

Compilier 클래스를 만들고 tokenize라는 메서드 함수를 만들어 구현했다.

 

우리 프로그래밍 언어에서 공백은 필요가 없기에 공백을 모두 제거하고, 명령어 단위로 묶어서 토큰을 만들었다.

그리고 보통 포인터 값 증가/감소 명령어인 . , 는 붙여서 많이 쓰이기 때문에 붙여서 토큰으로 인식해도 무방할 것 같아 그렇게 진행했다.

 

위의 예시의 HELLO WORLD 코드를 토큰으로 분해하면 다음과 같다.

['헬로에브리원투데이이즈', 
'디', '.....', '..', 
'앞으로인생에있어서', 
'디', '.....', '.....', 
'디', '.....', '.....', 
'디', '.....', '.....', 
'디', '.....', '.....', 
'디이', '디이', '디이', '디이', ',', 
'아무쓸모도없는거죠', 
'디', '..', '어', '디', ',', '어', 
'디', '.....', '.', '어', '어', 
'디', '.....', '....', '어', 
'디', '...', 
'앞으로인생에있어서', 
'디', '.....', '.....', '디이', ',', 
'아무쓸모도없는거죠', 
'디', '..', '어', 
'디', '.....', '.....', 
'앞으로인생에있어서', 
'디', '.....', '....', 
'디', '.....', '...', 
'디', '.....', '...', 
'디', '.....', '...', 
'디', '.....', '..', 
'디이', '디이', '디이', '디이', '디이', ',', 
'아무쓸모도없는거죠', 
'디', ',,,', '어', 
'디', ',', '어', 
'디', '..', '어', 
'디', ',,,,', '어', 
'디', ',,', '어', 
'오늘은여기까지하겠습니다']

 

AST 구현없이 실행시키기 

사실 여기서 의미를 부여하고 계층 구조를 만드는게 필요한가 싶었다.

토큰 자체가 하나하나 매우 간단한 명령어이고, 계층 구조를 만들 필요가 있는 건 반복문 뿐인데

계층 구조를 만드는거보다, 그냥 스택에다가 분기 명령어 위치 저장해놓고 쓰는게 더 편할 것 같았다.

    def excute(self, tokens):
        tape = [0]
        pointer = 0
        stack = []

        #checking syntax
        if not(tokens[0] == '헬로에브리원투데이이즈' and tokens[-1] == '오늘은여기까지하겠습니다'):
            print("Syntax Error!")
            exit(0)
        
        # for i in range(1, len(tokens)-1, 1):
        i = 1
        while i < (len(tokens) - 1):
            if tokens[i] == '디':
                pointer += 1
                if pointer >=  len(tape):
                    tape.append(0)
            elif tokens[i] == '디이':
                pointer -= 1
                if pointer < 0:
                    print("pointer underflow error!")
                    exit(0)
            elif tokens[i] == '어':
                # print(tape, pointer)
                print(chr(tape[pointer]), end="")
            elif tokens[i] == '어어':
                tape[pointer] = int(input())
            elif tokens[i] == '앞으로인생에있어서':
                if tape[pointer] == 0:
                    while not tokens[i] == '아무쓸모도없는거죠' and i < len(tokens):
                        i += 1
                else:
                    stack.append(i)
            elif tokens[i] == '아무쓸모도없는거죠':
                if tape[pointer] == 0:
                    stack.pop()
                else:
                    i = stack[-1]
            else:
                number = 0
                for char in tokens[i]:
                    if char == '.':
                        number += 1
                    else:
                        number -= 1
                
                tape[pointer] += number
            
            i += 1
        
        print('\0')

 

다시봐도 정말 쓰레기 같은 하드 코딩이다.

하지만 이걸 열심히 해봤자, 아무 도움이 안된다는 걸 알기에

심심풀이용으로 대충 만들어봤다.


실행 결과

잘 나온다

ㅋㅋㅋㅋㅋㅋㅋ

 

구현하면서 신기했던건, 문법이 간단하고 한 번 구조를 잘 만들어놓으니까 정말 프로그래밍 언어처럼 잘 동작한다.

 

중간에 다른거도 출력해보려고 코드를 바꿔봤는데 결과가 잘 안나와서,

내가 컴파일러 구현에 실수한게 있었나 하고 봤는데 알고보니까 코드에 띄어쓰기 오류가 있었다.

이 정도면 진짜 프로그래밍 언어가 아닐까?

 

물론 이걸 컴파일러라고 부르기엔 애매하고, 인터프리터라고 하는 게 더 알맞은 표현 아닐까 싶지만 이 정도는 그냥 넘어가자.

 

챗 지피티한테도 알려줬는데 잘 못한다.

너무 어려운걸 시켰나보다.


마무리하며

사실 시험기간에 그냥 심심해서 떠올린 아이디어가 생각보다 공부할게 많은 주제였던것 같다.

그리고 오토마타에서 추상적으로 다루기만 했던 내용들을 실제로는 이런거 만들때도 쓰이는구나 라는 것도 배울 수 있었다.

용두사미처럼 끝난 거 같은데 많은 분들이 재밌어 해주셨으면 좋겠다.

 

아 그리고 교수님 제가 많이 존경합니다.

이번학기 수업 재밌게 잘 들었습니다.

문제가 되면 지우겠습니다.

1편을 읽고 오신 분들이라면, 오토마타가 무슨 과목인지 감을 잡았을거라 생각한다. (그랬으면 좋겠다)

2편에선 '튜링 머신과 튜링 완전'에 대해 설명하고

3편에서 '난해한 프로그래밍 언어 만들기' 에 대해 설명해보려고 한다.


튜링 머신이란?

1편에서 괄호 문법 예시를 통해 문제를 해결하는 방법 (문제 => 문장과 언어 => 기계 )에 대해 설명했고,

PDA를 통해 '추상화한 기계' 에 대한 예시를 들어봤다.

 

튜링 머신 또한 특정한 문제를 해결하기 위한, 추상화 된 기계이다.

그러나 PDA보다 조금 더 강력한(?) 기계라고 할 수 있다.

  • 메모리로 테이프(가로로 길게 연결된 형태의 메모리)를 가진다. (길이는 무한하다고 가정한다)
  • Control Unit은 임의의 상태(State)를 가지며, 테이프의 값을 한 번에 하나씩 읽는다.
  • 테이프의 값을 읽은 후에는 상태를 변경하며, 테이프에 값을 새로 입력하고 테이프를 좌, 또는 우로 한 칸 이동시킬 수 있다.

 

튜링머신은 스택을 사용하는 PDA와는 다르게 좌우로 자유롭게 움직일 수 있는 메모리인 테이프를 사용한다.

그리고 이 테이프의 길이는 이론상 무한하다고 가정하기 때문에, PDA로는 해결할 수 없는 다양한 문제를 해결할 수 있다.


덧셈 계산기

임의의 숫자 2개가 주어졌을 때 덧셈을 하는 기계를 만들어야한다고 생각해보자.

이 기계는 단순히 문자열이 문장에 속하는 지 판단하여 끝나는 간단한 기계가 아니다.

예, 아니오 응답이 아닌 덧셈 결과를 응답해야한다.

우선 튜링 머신으로 덧셈 계산기를 구현하기 위해 덧셈 연산을 간단화해보자.

숫자를 단순하게 1의 연속으로 변경해보자. (이진법이 아님에 주의하자, 2는 1 두개, 3은 1 세개이다.)

 

그렇다면 이제 튜링 머신은 다음과 같은 연산을 진행하면 된다.

  1. 1을 만나면 1을 그대로 입력하고 오른쪽으로 이동한다.
  2. +를 만나면 그 자리에 1을 입력하고 오른쪽으로 이동한다.
  3. 테이프에서 공백을 만날때까지 1,2를 반복한다.
  4. 공백을 만나면 왼쪽으로 이동 후 1을 공백으로 변경한다.


보편 튜링 머신

튜링 머신은 강력한 기계이지만, 할 수 있는 일이 정해져있다는 한계가 존재한다.

 

  1. 1을 만나면 1을 그대로 입력하고 오른쪽으로 이동한다.
  2. +를 만나면 그 자리에 1을 입력하고 오른쪽으로 이동한다.
  3. 테이프에서 공백을 만날때까지 1,2를 반복한다.
  4. 공백을 만나면 왼쪽으로 이동 후 1을 공백으로 변경한다.

우리는 튜링 머신을 설계할때 앞서 설계한 것처럼, 특정한 상황에서의 동작만 정의할 수 있고, 이는 유한하다.

만약 우리가 설계한 범위를 벗어난 상황을 만나면(중간에 -와 같은 정의되지 않은 값을 만나거나, 4번까지 수행이 끝난 상황)

이 상황을 halt(정지)라고 부르며, 튜링 머신은 더 이상 동작할 수 없다.

 

이렇게 제한적인 튜링 머신의 사용 문제를 해결하기 위해 '보편 튜링 머신'이라는 게 등장했다.

보편 튜링 머신은 튜링 머신을 시뮬레이션 할 수 있는 머신이다.

 

1. 위에서 정의한 튜링 머신의 상황에 따른 동작 결과(1,2,3,4)를 표로 만들어 보편 튜링 머신에 저장해둔다.

2. 튜링 머신으로 실행시켜볼 내용(2+3)도 저장한다.

3. 저장된 상태표를 읽어 튜링 머신의 동작 과정을 그래프로 만들어낸다.

4. 실행 시킬 내용을 읽으며 실제로 수행해야할 작업을 그래프를 순회하며 수행한다.

 

이 과정을 통해 보편 튜링 머신은 모든 튜링 머신의 기능과 동작을 흉내낼 수 있다.

 

여기서 상태표를 우리가 설계하는 명령어, 실행시킬 작업을 데이터라고 하면,

명령어와 데이터를 함께 메모리에 적재하고 실행시키는 기계. 즉 폰 노이만 구조 컴퓨터의 원형이라고 할 수 있다.


튜링 완전

보편 튜링 머신에 튜링 머신의 상태표와 실행시킬 작업을 저장해 튜링 머신을 시뮬레이션 해볼 수 있다고 했다.

 

이때 상태표를 명령어(프로그래밍 언어), 실행시킬 작업을 데이터(실행시킬 코드)라고 하면,

즉 우리가 쓰는 폰 노이만 구조 컴퓨터를 추상화했다고 볼 수 있다.

 

그렇다면 튜링 머신과 동치인 언어, 즉 튜링 머신과 동일한 기능을 할 수 있는 언어가 있다면 "튜링 완전한 언어"라고 부른다.

그리고 튜링 머신의 기능은 사실 그리 복잡하지 않다.

  • 특정한 조건을 판단할 수 있다(조건 분기문이 존재한다 ex: if, goto...)
  • 임의의 메모리를 가리키는 포인터가 존재하고, 해당 포인터를 통해 메모리의 값을 변경할 수 있다.

현존하는 대부분의 프로그래밍 언어는 튜링 완전하다고 할 수 있다.

그러나 튜링 머신은 테이프의 길이가 무한하다고 가정하지만, 현실세계에서 무한한 메모리를 구현할 수는 없으므로

만약 메모리가 무한하다면 튜링 완전한 경우를 "느슨한 튜링 완전"이라고 부른다.


튜링 완전한 프로그래밍 언어 만들기

 

드디어 1편에서 설명한 '난해한 프로그래밍 언어 만들기' 에 대해 이야기할 차례이다.

 

BrainFu**

가장 작은 컴파일러로 구현할 수 있는 "완전 튜링한 언어 만들기" 를 목표로 해서 나온 언어이다.

이 프로그래밍 언어는 단 8개의 명령어만을 가지는데 다음과 같다.

> 포인터 증가
< 포인터 감소
+ 포인터가 가리키는 값 1 증가
- 포인터가 가리키는 값 1 감소
. 포인터의 값을 ASCII 문자로 출력
, ASCII 코드 값을 입력받아 포인터 메모리에 저장
[ 반복문 시작
] 반복문 종료(포인터의 값이 0이 아니면 ]로 돌아가 반복)

 

++++++++++
[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++++++++++++++.------------.<<+++++++++++++++.>.+++.------.--------.>+.

 

이건 BrainFu** 언어로 Hello, world!를 출력하는 코드이다!

 

쓰레기 같은 가독성으로 어그로를 끌어 유명해졌지만, 이 언어는 "튜링 완전한 언어"이다.

임의의 테이프(메모리)를 가리키고 있으며, 메모리의 값을 수정 가능하고, 분기 명령어가 존재한다.

 

사실 이것 외에도 "엄랭", "재즈랭" 등 온라인 상에서 유행하는 밈을 이용해 '난해한 프로그래밍 언어 만들기'가 꾸준히 진행되고 있다.

https://esolangs.org/wiki/Main_Page

 

Esolang, the esoteric programming languages wiki

This wiki is dedicated to the fostering and documentation of programming languages designed to be unique, difficult to program in, or just plain weird. Why not join us on IRC? For readers Featured language Thue is an esoteric programming language based aro

esolangs.org

위키도 있다고 하니 궁금한 사람들은 찾아보자.

똥을 굳이 찍어먹어봐야하나

 

다음 글에선 "직접 튜링 완전한 언어" 를 만들고 컴파일러를 재작하는 과정을 알아보자.

왜 하냐고요?

 

그야 재밌으니까

ㅋㅋ

 

 

 

 

이번학기 전공으로 '오토마타' 라는 과목을 배웠다.

이번 학기 들었던 전공 중에서 가장 재미있게 들었던 과목 중 하나이지만,

자료구조, 어셈블리언어 같은 과목들에 비해 이걸 배워서 도대체 어따쓰지? 라는 생각이 계속해서 드는 과목이었다.

 

나만 이런 생각을 하는 건 아닐거라고 생각하고, 몇가지 글 들을 인터넷에서 뒤적거려봤고,

오토마타 과목의 마지막에 다루는 튜링 머신의 개념을 프로그래밍 언어와 컴파일러 설계에 활용할 수 있다는 사실을 알았다.

 

https://kciter.so/posts/crafting-esolang/#%ED%8A%9C%EB%A7%81-%EA%B8%B0%EA%B3%84

 

난해한 프로그래밍 언어 만들어보기

잉여 코딩이라는 말을 들어본 적 있는가? 잉여 코딩은 돈 버는데엔 쓸모없지만 취미로 즐겁게 코딩하는 것을 의미한다. 최근 몇 년 사이엔 대부분 업무와 직간접적으로 연관이 있는 코딩만 했는

kciter.so

그래서 위의 '난해한 프로그래밍 언어 만들어보기' 라는 글을 읽고

튜링 완전한 프로그래밍 언어와 컴파일러를 만드는 과정을 써보려고 한다.

 

이 시리즈를 통해 오토마타 과목을 수강한 학우분들은 '오토마타를 이런식으로 사용할 수 있구나' 라는 사실을

오토마타를 수강하지 않았지만 고민하는 학우분들에게 오토마타가 어떤 과목인지 대략적으로 전달할 수 있었으면 좋겠다.


1. 오토마타란?

Automata

오토마타는 추상적인 계산기로, 정해진 규칙에 따라 입력을 읽으면서 내부의 상태를 바꾸고 결과를 출력하는 기계의 수학적 모델이다. 단어 'automaton'은 '스스로 작동하는'을 뜻하는 그리스어 단어 'αυτόματος'에서 유래했으며, 한국어에서는 보통 복수형 'automata'를 음차한 '오토마타'로 지칭한다.  - 나무위키 -

 

여기서 핵심은 '기계의 수학적 모델'이다.

오토마타 과목에선 한 학기 동안 여러 문제를 해결할 수 위한 추상화되어 있는 기계들을 배운다.

 

여기서 문제는 보통 "어떤 문장이 한 언어에 속하는가?"로 먼저 추상화된다. 

 

문제 => 문장과 언어 => 기계

 

오토마타는 위와 같이 문제를 문장과 언어로 추상화해서 해결해주는 기계를 설계하는 과목이다.

이렇게만 이야기하면 하나도 이해가 안 가니 예시를 들어보자.


중첩된 괄호 문제

어떤 프로그래밍 언어의 문법에 괄호가 존재한다고 하자. 

실제 프로그래밍 언어의 괄호 문법을 적용하면 너무 복잡해지니, 조금 쉬운 괄호 문법을 예시로 들어보자.

 

이 프로그래밍 언어에선 아래와 같이 괄호를 사용할 수 있다.

  1. 괄호는 중첩해서 사용이 가능하다.
  2. 열린 괄호의 갯수만큼 닫는 괄호가 존재해야한다.
  3. 닫힌 괄호가 열린 괄호보다 먼저 나와선 안된다.

ex)

  • (( )) => O
  • ((( ))) => O
  • (( )))) => X

이제 이 프로그래밍 언어로 만들어진 코드의 괄호 문법을 검사하는 컴파일러를 우리가 만들어야한다고 하자.

컴파일러는 해당 프로그래밍 언어의 괄호 문법을 어떻게 효율적으로 검사할 수 있을까?

 

문제를 언어로 추상화하기

먼저 코드를 한 줄 씩 해석하며 열린 괄호와 닫힌 괄호를 만날때마다 각각 알파벳 a,b로 바꿔 문자열을 만들어보자.

그렇다면 올바른 괄호 구조를 사용한 코드의 경우 aabb, aaabbb 와 같은 구조의 문자열이 만들어질 것이다.

 

그렇다면 이제 어떤 언어를 다음과 같은 문자열들의 집합이라고 정의해보자.

 

이러면 프로그래밍 언어의 괄호 문법을 검사하는 과정은,

코드를 해석하며 만든 문자열(aabb, aabbb)이 위의 언어에 속하는가? 로 추상화된다.

정확하게는 문자열이 언어가 정의한 집합에 속하는가? 이지만 안 중요하니까 넘어가자.
어떤 문자열이 언어가 정의한 집합에 속하는 경우 그 문자열은 언어의 문장이다라고 이야기한다.

 

 

이제 별다른 설명이 없어도 괄호 문법을 만족하는 언어를 컴파일하며 만든 문자열(ex: aaabbb)은,

반드시 위의 언어에 속할거라는 사실을 우리는 쉽게 유추할 수 있다.

 

문장이 언어인지 판독하는 기계 만들기

그렇다면 다음은 어떤 유한한 길이의 문자열(ex: aaabbb)이 주어졌을때,

해당 문자열이 위의 언어에 속하는지 판단하는 기계를 설계하면 된다.

 

한 학기 동안 여러 종류의 기계를 배우지만 위와 같은 언어는

PDA(Pushdown Automata)를 통해 쉽게 판독할 수 있다.

 

PDA는 위와 같이 input file, Control unit, Stack 3가지 구조로 이루어진 간단한 기계이다.

조금 더 자세히 살펴보면 다음과 같은 기계를 추상화 한 것을 PDA라고 부른다.

 

  • 스택을 메모리로 가진다. (스택의 메모리는 무한하며 우리가 원하는 내용을 저장할 수 있다고 가정한다.)
  • Control unit은 input file에서 문자(알파벳)을 한번에 하나씩 읽어온다.
  • Control unit은 input file에서 읽은 알파벳과, 스택의 탑에 위치한 정보를 바탕으로, 다음에 어떤 행동을 할 지 결정한다.

 

우리는 이제 input file에 우리가 언어에 속하는지 아닌지 판단할 문자열(aaabbb)을 넣고,

문자열을 하나씩 읽으며 스택을 사용해 판단을 내리도록 Control unit을 설계하면 된다.

 

우선 input 파일에 우리가 판단할 문자열(aaabbb)를 넣고,

Stack의 맨 위에는 스택의 시작 상태를 의미하는 임의의 값(Z)를 넣는다.

 

Control Unit은 Input의 알파벳을 앞에서부터 하나씩 읽으며 다음과 같은 연산을 수행한다.

 

1. a를 읽는다면 스택에 1을 push한다.

2. b를 읽는다면 스택의 맨 위에 위치한 값을 pop한다.

 

3. input 파일에서 더 이상 읽을 게 없을때 스택의 탑에 시작상태(Z)가 남아있다면, input 문자열은 언어에 속한다!

 

우리는 위의 과정을 통해 앞서 정의한 프로그래밍 언어의 괄호 문법을 판단하는 추상화된 기계를 설계했다.

물론 지금까지의 설명은 오토마타를 듣지 않은 사람들의 쉬운 이해를 위해 여러 부분을 생략했다.

 

설계한 기계의 문제점

  • 닫힌 괄호가 열린 괄호보다 먼저 나와선 안된다.

이 기계는 위의 정의한 괄호 문법에 맞지 않아도,

열린괄호와 닫힌 괄호의 총 개수만 맞다면 문제가 없다고 판독한다.

 

예를 들면 (()()) 와 같은 경우 => aababb로 번역되고,

우리가 정의한 기계는 이 문자열을 언어에 속한다고 판독한다.

 

물론 우리가 아는 프로그래밍 언어의 괄호 문법은 오히려 저게 옳은 문법이라고 생각하지만,

문제는 우리가 만든 기계가 처음 의도한 것과 다른 결과를 만든다는 점에 있다.

 

사실 PDA에서 중요한 상태(State)를 빼고 설명하다보니, 조금 힘들어졌다.

 

실제로는 Control Unit이 매 순간 특정한 상태(State)를 가지고,

input을 하나씩 읽을때마다 상태를 변경하거나, 유지하며 Stack의 탑을 변화시킨다.

 

즉 Control Unit은 (input 알파벳, 현재 상태, 스택의 탑) 이라는 3가지 정보를 이용해,

(다음 상태, 스택의 탑) 이라는 결과를 결정하는 일을 한다.

 

 

이 과정을 함수나 그래프로도 표현할 수 있으며,

오토마타 과목에선 PDA를 포함한 여러 기계를 함수와 그래프로 설계하는 방법을 배운다.


마무리

이 글을 쓴 이유는 "PDA가 무엇인가"를 설명하기 위해서가 아닌 "오토마타가 어떤 과목인가"에 대한 대략적인 설명과,

다음 글에서 설명할 "튜링 머신"을 이해하기 위한 전반적인 배경지식을 설명하기 위해서이다.

 

그래서 기계와 언어에 대해 설명할때 상태(State)와 같은 내용들을 과감히 생략했다.

어차피 오토마타를 들은 분들은 무슨 소리하는지 다 알거고,

아직 안 들은 사람들은 대충 이런 과목이구나 정도만 알아도 도움이 될 거라고 생각했다.

 

다음 글에선 '난해한 프로그래밍 언어' 를 설계하기 위한 과정과 튜링 머신에 대해 알아보자.

 

 

 

 

사실 군대에서 개발 공부를 하는건 사실 좋은 생각은 아닌 듯 하지만

1년 9개월을 내다 버리기 싫으면 뭐라도 해야만 합니다.

 

저는 군대에서 보내야 하는 1년 반 이상의 시간을 그냥 날려버린다면 너무 억울할 것 같다고 생각했습니다.

그래서 뭐라도 얻어가는게 있으면 좋겠다 라고 생각했고, 자기계발 여건이 좋다고 들은 공군에 지원했죠.

 

아무래도 훈련소에서부터 다양한 사람들을 만나면서 공군에 오길 잘했다는 생각이 들었습니다.

(평생 한 번도 못 봤던 서울대생들이랑 카이스트 학생들을 군대에서 다 본 것 같아요.)

 

그리고 그 사람들과 이야기를 나눠보면서, 그 사람들끼리 이야기 나누는 걸 들으면서

지금까지 해왔던 노력과, 결과의 격차가 많이 느껴졌습니다.

 

그리고 오히려 군대가 기회가 될 수 있다고 생각했습니다.

군대에서 만큼은 밖에서 어떤 사람이었든 같은 조건에서 같은 시간을 보내야하니까요.

지금까지 벌어졌던 격차를 좁힐 수 있는 유일한 기회일거라 생각했고,

그래서 처음 한 6개월간은 자기계발에 엄청 매진했던 기억이 납니다. (물론 해야할 일부터 열심히 하고요)

 

또 SNS, 깃허브 등을 보면 군대에 오지 않고 밖에 있던 동기들과 격차가 벌어지는 걸 느끼고,

더 불안함을 느껴서 그랬을지도 모릅니다.

 

아침에 일어나자마자 점호 끝나고 아침 먹고 독서실로 가서 영어 단어를 외우다가 출근을 했습니다.

오후 일과가 끝나자마자 체련시간에 체단실로가서 운동을 하고

저녁 먹고 씻고 개인정비시간이 되자마자 사지방으로 가서 3시간 내내 코딩 공부를 했습니다.

청소와 저녁 점호가 끝나곤 자기전까지 책을 읽었습니다.

 

자기계발할 시간이 사라지는게 싫어서 한 달 두번씩 주어지는 평일 외출도 거진 안 나갔고,

코로나 걸려서 격리되면 공부 못하니까, 몸이 아파도 숨기기까지 했었습니다.

 

결국 이렇게 2,3달 정도 살다보니까 몸도 다치고 마음도 더 힘들어졌습니다.

 

뭐든지 적당히가 중요하다고 생각합니다. 다 때려치고 자기계발만 하는건 좋은 생각이 아닙니다.

군대에서 무언가를 얻어갈 수 있다는건 정말 좋은 일이지만,

여유 시간을 자기계발에만 쓰려는건 미친짓입니다. 오래 하지도 못하고요.

 

그 안에서 찾을 수 있는 즐거움과 행복을 틈틈히 찾아보시길 추천합니다.

군대라고 해도 24시간 내내 힘들기만 하진 않습니다.

동기들과 추억도 쌓고 그 안에서 할 수 있는 건강한 취미생활도 찾아보는걸 추천드립니다.

 

전역 이후에 이어갈 수 있는 취미생활이면 더 좋습니다.

전역까지 남은 시간동안 그걸로 목표를 세울 수 있게되는데, 목표가 생기면 시간도 더 잘 갔습니다.

 

(저는 부대에 있는 교회 찬양팀에 들어가서 피아노랑 기타 연습을 하는 취미가 생겼습니다.

전역하고 학교 밴드 동아리에 들어가고 싶다는 목표가 생기니까 열심히 하게 되더라고요)

 

그래도 자기계발을 아예 안하는 것보단 당연히 하는게 좋겠죠.

저는 저에게 주어진 1년 9개월의 시간을 이렇게 사용했습니다.

 

저는 훈련소, 특기학교 + 자대와서 적응하는데 3달 정도 걸렸습니다.

 

입대 3달 뒤부터 알고리즘 공부를 시작해 2~3개월 정도 했습니다.

 

이것이 취업을 위한 코딩 테스트다 with 파이썬 - 예스24

 

이것이 취업을 위한 코딩 테스트다 with 파이썬 - 예스24

나동빈 저자의 유튜브 라이브 방송 https://www.youtube.com/c/dongbinnaIT 취준생이라면 누구나 입사하고 싶은 카카오 · 삼성전자 · 네이버 · 라인!취업의 성공 열쇠는 알고리즘 인터뷰에 있다!IT 취준생

m.yes24.com

휴가 때 이 책 하나 사서 일과 시간 남는 시간에 정독하고 일과 끝나고 사지방에서 문제를 풀었습니다.

(물론 좋은 분위기의 부대에 일이 없는 부서라서 가능했던 일이었습니다.)

 

백준 골드5였던 백준 티어가 플레티넘이 되고 난 후부턴 개발 공부에 다시 시간을 쓰고 싶어 알아봤습니다.

개발 공부에 사용할 수 있는 온라인 ide 찾기에 1~2달 정도 쓴거 같네요.

이땐 입대 전에 장고로 만든 프로젝트를 리팩토링 하면서 온라인 IDE 사용법에 익숙해졌습니다.

 

그 이후엔 백엔드에 대해 더 알아보고 싶어졌고 spring을 배워보고 싶어

영한선생님 JPA와 스프링 강의를 듣는데 3~4개월 정도 썼습니다.

 

Java의 정석 - 예스24

 

Java의 정석 - 예스24

최근 7년동안 자바 분야의 베스트 셀러 1위를 지켜온 `자바의 정석`의 최신판. 저자가 카페에서 12년간 직접 독자들에게 답변을 해오면서 초보자가 어려워하는 부분을 잘 파악하고 쓴 책. 뿐만 아

m.yes24.com

그리고 JAVA 기본기가 부족하다는 생각이 들어 java의 정석을 정독하고 정리해봤습니다.

 

그리고 입대전에 만들었던 기존 장고 프로젝트를 spring boot로 리팩토링해보며 스프링과 JPA 공부를 했습니다.

그리고 휴가를 벌어야 해서 e러닝과 자격증 공부도 했습니다.

여기에 합쳐서 3~4달 정도 사용한 것 같네요.

 

그리고 그걸 이어서 후임으로 들어온 프론트엔드 개발자와 동기 디자이너와 함께 팀 프로젝트를 진행했습니다.

 

배포까지 했더니 군생활이 끝나더라고요.

저의 군생활 코딩 공부는 이렇게 진행됐습니다.

 

추천을 드리자면 자기계발비로 개발 책 사서 틈틈히 읽어보는걸 추천드립니다.

1년 12만원씩 지원 나오는데 평소에 돈 주고 사기 아까웠던 개발 책들 자기계발비로 잔뜩 사고 지금도 읽고 있습니다.

(위에 나온 자바의 정석과 이것이 코딩테스트다도 자기계발비로 샀습니다.)

 

그리고 개발공부 말고도 자격증 공부도 추천드리는 자기계발 중 하나입니다.

공군 기준으로 정보처리기능사(산업기사) + SQLD + 리눅스마스터 이렇게 3개 따면 휴가 이틀이 나옵니다.

근데 이 3가지가 내용이 겹치는 부분들이 있고, 기본 지식이 조금만 있으면 편하게 딸 수 있는 자격증들입니다.

저는 이 자격증이랑 군 e러닝으로만 포상휴가 6일을 벌었습니다.

 

주변에선 공무원 시험, 수능 준비 같은 것도 하던데, 수험 생활은 정말 힘들어 보였습니다.

굳은 결의로 하시겠다면 말리진 않겠습니다.

하지만 시간도 남는데 해볼까 라는 마음으로 하는 사람은 99프로 다 망했던 것 같습니다.

이건 하려면 정말 열심히 할 각오로 하셔야 할 것 같습니다.

애초에 밖에서 하는 수험공부도 좋은 결과가 못 나온 사람들이 많은데, 군대에서 하는건 정말 큰 노력이 필요합니다.

 

그리고 적당한 운동도 아주 좋은 자기계발입니다.

앞서 말한 전역 이후에도 이어갈 수 있는 취미생활이라고도 볼 수 있고,

군대에서 만큼 규칙적으로 살면서 운동할 환경이 주어지는 곳이 또 없습니다.

다만 다치지 않는 선에서 하시길 응원합니다.

(제가 본 군생활 부상의 90프로 이상은 풋살이랑 헬스하다 일어났습니다)

 

그리고 또 군생활 팁을 드리면 휴가때 공군 호텔, 육군 호텔 잘 사용해 보는 걸 추천드립니다.

공군 호텔은 부산에 있고 직접 가봤는데, 시설이 엄청 좋은 편은 아니지만

가성비 좋은 해운대 오션 뷰 호텔이라 괜찮았습니다.

 

육군호텔은 23년 3월에 용산에 지어졌다는데, 직접 가보진 않았지만 후기가 다들 정말 좋고

육군 현역 병, 타군 현역 병 순으로 할인된 가격에 예약이 가능하니까 휴가때 가보는 것도 추천드립니다.

 

그 외에도 아웃백, 영화관 놀이공원 등도 할인이 많은데 찾아보고 휴가때마다 도장깨기 하는 것도 추천합니다.

근데 롯데월드, 영화관 같은건 대학생 할인, 카드 할인 같은게 군인 할인보다 싼 경우 많으니까 알아보고 가세요.

 

나라 지키느라 고생 많으십니다. 25년도 26년도 결국 반드시 옵니다.

전역하는 그날까지 오늘도 여러분들을 응원합니다.

화이팅.

그냥 참고 하라고 ㅋㅋㅋ

라고 하면 안되겠죠??

 

온라인 IDE를 잘 사용하는 법, 사용하는 팁 그리고 특히 스프링 부트를 IDE로 돌리기 위해 참고할 점들에 대해 알아보겠습니다.

 

전편에서 말했듯 저는는 구름 IDE를 사용해서 스프링 부트 위주로 공부를 했습니다.

그래서 아마도 구름 IDE에 기준이 맞춰져 있을 텐데, 다른 IDE를 고른 사람들은.. 뭐 아쉬운거죠

 

1. 프로젝트 시작하기

1-1. 환경설정 하는 법

사실 이건 예전에 쓴 글이 있긴 한데 참고하면 좋을 것 같습니다.

사지방에서 Spring boot 공부하기 #1 - 개발환경 설정

 

사지방에서 Spring boot 공부하기 #1 - 개발환경 설정

인프런 김영한 님의 "스프링 입문 - 코드로 배우는 스프링부트, 웹 MVC, DB 접근" 기술 강의를 듣고 정리해보려 합니다. 개발환경 - GOORM IDE 1. 구름 IDE에서 컨테이너 생성 구름 IDE 접속 -> 새 컨테이

thankyou-ddabong-dochi.tistory.com

 

나는 스프링 말고 Django 같은거 쓰고 싶다! 그러면 이 글을 봅시다.

https://thankyou-ddabong-dochi.tistory.com/66

 

사지방에서 웹 백엔드 공부하기 #1 개발환경 설정 (Goorm IDE)

입대한지 벌써 6개월차에 접어드는 지금 맨 처음 입대할때의 목표를 다시 살펴봤다. 1. 꾸준히 운동하기 2. 코딩 공부하기 ( 백준 플레찍기 ) 3. 영어 공부하기 1은 하고 있고 2 번 목표는 생각보다

thankyou-ddabong-dochi.tistory.com

 

다만 1년 정도 전에 쓴 글이므로 업데이트를 좀 해보자면 보통 스프링 공부를 시작할 때 영한 선생님 강의를 많이 듣는데,

영한 선생님의 스프링 입문 강의에선 h2를 설치한 이후 Server모드를 이용해 진행합니다.

 

그러나 아직 리눅스에 익숙하지 못해 wget도 몰라서 h2를 설치하지 못했던 저는

goorm ide에서 기본으로 제공해주는 MySQL을 사용하고  phpmyadmin을 설치하고 접속해 db를 확인했고,

위의 글도 그 방법을 소개합니다.

 

나중에 안 사실이지만 spring boot는 간단한 설정으로 h2 database를 설치 없이 사용할 수 있도록 지원해줍니다.

h2를 사용하면 phpmyadmin을 설치할 필요도 없이 h2-console을 편하게 db 내용을 확인할 수 있습니다.

H2가 뭐죠?

java 기반 오픈 소스 RDBMS이다. 다음과 같은 주요 기능을 가집니다

  • 매우 빠름, 오픈 소스, JDBC API 지원
  • 두가지 연결 모드(Embedded, Server) 제공, 디스크 혹은 메모리 기반 데이터 베이스
  • 브라우저 기반 콘솔 어플리케이션 제공
  • 2.5MB jar 파일 정도의 작은 리소스 사용

두 가지 연결 모드에 대해 더 알아보면

Embedded 모드와 Server(Remote)모드의 메모리 구조

1. Embedded mode

H2 DB를 사용할 어플리케션의 JVM 환경 내에서 실행시킵니다.

가장 빠르고 간단한 연결 모드이지만 DB를 하나의 어플리케이션 내에서만 접속할 수 있습니다.

스프링 부트를 사용할 경우 H2를 다운로드 하지 않아도 됩니다.

 

2. Server mode

H2 DB를 어플리케이션이 원격으로 연결해 사용합니다. (Remote or Client/Mode 모드라고도 불립니다.)

모든 데이터 통신이 TCP/IP 프로토콜을 통해 전달되므로 Embedded 모드보다 속도가 느립니다.

하지만 많은 어플리케이션이 이 서버를 통해 동시에 데이터베이스와 연결할 수 있습니다.

H2를 다운받은후 터미널을 통해 별도로 실행시켜줘야 합니다.

 

그리고 특정한 상황(빠른 프로토타입 제작, 테스트, 읽기 전용 데이터) 등 데이터의 영구 보존이 필요하지 않은 경우

in-memory DB를 사용할 수 있습니다.

이 경우 파일 시스템이나 네트워크를 사용하지 않고, 어플리케이션 내부에서만 데이터를 보존합니다.

따라서 어플리케이션을 재실행하면 데이터가 삭제됩니다.

 

물론 프로젝트 테스트용 데이터 규모가 크거나, 데이터를 다른 사람들과 공유해야하거나, 실사용을 염두에 두고 있다거나... 하다면

DB 설치 및 세팅도 해보면 좋겠지만, 혼자 강의 듣고 따라하기, 사지방에서 프로젝트 개발하기 정도의 용도라면 h2 사용을 추천합니다.

 

개발시에는 H2를 따로 설치할 필요가 없는 Embedded mode, 테스트시에는 빠른 연결을 위한 in-memory 모드를 사용하길 추천합니다.

 

설정 방법에 대해선 공식문서와 아래 블로그를 참고하면 좋을 듯 합니다.

https://h2database.com/html/cheatSheet.html

 

H2 Database Engine

Using H2 Documentation Reference: SQL grammar, functions, data types, tools, API Features: fulltext search, encryption, read-only (zip/jar), CSV, auto-reconnect, triggers, user functions Embedded jdbc:h2:~/test 'test' in the user home directory jdbc:h2:/da

h2database.com

https://velog.io/@jinny-l/H2-DB의-3가지-모드와-사용법-Server-Mode-Embedded-Mode-In-Memory-Mode

 

H2 DB의 3가지 모드와 사용법 - Server Mode, Embedded Mode, In-Memory Mode

💾 H2 DB H2 DB는 3가지 모드가 있다. 각 모드 별 URL이나 커넥션 풀 등 관련 상세 내용은 공식 문서에서 확인 가능하다. 본글에서는 3가지 모드와 사용법에 대해 공부한 내용을 정리했다. > H2 DB의 3

velog.io

 

1-2. 빌드 속도 높이기

터미널에서 명령어를 통해 빌드를 해야 하는 우리의 구름 IDE는 기가 막히게 느린 빌드, 실행 속도를 체감할 수 있습니다.

Django의 경우는 Python을 사용하기 때문에 따로 빌드후 실행할 필요가 없지만,

스프링은 이런 저런 라이브러리를 추가하고, 프로젝트 규모가 커질 수록 빌드하는데 시간이 오래 걸렸습니다.

(나중가면 빌드 한 번에 2분, 실행 한 번에 2분이 걸리는 미칠듯한 속도를 보여줍니다)

스프링 강의 같은거 보면 인텔리제이에 뭐 어쩌구 저쩌구 설치하더니 버튼 하나 딸깍 누르면 실행되던데,

구름 IDE는 실행 버튼 누르면 이상한 터미널이 하나 더 나오더니 강의 멈춰놓고 2분씩 기다려야하는데 그냥 정신이 아득해집니다.

 

프로젝트 하나 실행하는데 이렇게 오래 걸리는 이유에 대해 알고 싶다면면

Spring Boot 프로젝트에서 사용하는 빌드 도구인 gradle에 대해 알아야합니다.

 

Gradle이란?

오픈 소스 빌드 자동화 도구

빌드, jar 파일 만들기, 테스트와 같은 과정을 자동화 해주는 빌드 도구입니다.

 

우리의 goorm ide도 스프링 부트 프로젝트를 만들때 gradle을 사용하도록 설정할 수 있습니다.

그리고 최근 나오는 대부분의 강의, 자료들은 빌드 도구로 gradle을 사용하는 추세입니다.

 

goorm ide의 gradle 프로젝트에서 위의 사진의 실행 버튼을 누르면 다음과 같은 명령을 실행합니다.

gradle build && java -jar ${java.set.build.path}libs/*.jar

 

gradle build를 통해 jar 파일을 만들어 내고, 만든 jar 파일을 실행해 스프링 어플리케이션을 실행하는 명령어입니다.

그런데 우리의 구름 IDE의 스프링부트 프로젝트는 java8과 스프링부트 2.0.1 을 기본 버전으로 하고 있습니다.

 

최근 나오는 대부분의 강의, 자료들과 버전이 맞지 않으므로 업그레이드를 해줘야하는데,

저 실행 버튼을 사용하려면 gradle도 스프링 부트 버전에 맞도록 다시 설치하고 설정해야 합니다.

 

그래서 공식문서는 gradle wrapper를 이용해 빌드하는 것을 추천하고 있습니다.

gradle wrapper #gradle/wrapper 폴더 생성(gradle-wrapper.jar, gradle-wrapper.properties)
./gradlew build #gradle wrapper 사용해 빌드

gradle wrapper 명령어를 이용하면 gradle/wrapper 폴더와 설정 파일을 생성할 수 있습니다.

 

## gradle-wrapper.properties ##

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip #이 부분 버전만 변경해주자
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

/gradle/wrapper/gradle-wrapper.properties 파일에서 gradle 버전만 변경해주면 됩니다.

그리고 그 후엔 gradle 대신 ./gradlew 명령어를 통해 gradle wrapper를 이용한 build, test 등의 작업을 수행할 수 있습니다.

 

이렇게 gradle wrapper를 사용하면 다음과 같은 장점이 있습니다.

  1. 프로젝트의 gradle 버전 표준화
  2. 다른 사용자에게 동일한 gradle 버전 제공
  3. 다른 환경에서도 동일한 gradle 버전 제공

그리고 한 번 빌드를 한 이후부터는 빌드 속도가 유의미하게 줄어드는데

이는 gradle deamon 덕분에 변경된 파일만 다시 빌드하기 때문입니다.

 

gradle deamon은 첫 빌드 이후부터 백그라운드에서 실행되며 변경되는 파일을 감지합니다.

백그라운드에서 실행되고 있으므로 다음 빌드를 실행할시 jvm을 다시 실행시킬 필요가 없으며,

변경된 부분만 다시 빌드해 속도를 현저하게 낮춰줍니다.

 

그니까 처음 접속하면 ./gradlew build 먼저 실행해서 gradle daemon을 활성화 시킨 후 공부를 시작하면,

그 이후 빌드 속도가 매우 빨라집니다.

 

그리고 영한 선생님 JPA 강의처럼 테스트 코드를 짜면서 진행한다면 gradle 빌드시 test가 task에 들어가도록 설정하는 경우가 많은데,

이 경우 테스트 때문에 빌드 속도가 느려집니다.

그럴땐 ./gradlew build -x test 명령어를 통해 테스트를 제외하고 빌드해 속도를 높일 수 있습니다.

 

반대로 테스트 코드만 실행 시키려면 ./gradle test 명령어를 사용하면 되며,

./gradle test --tests "클래스명" 과 같이 사용해 특정 테스트만 실행하는 것도 커맨드 라인에서 할 수 있습니다.

 

그래도 속도가 느리다면 --parellal 옵션과 gradle daemon 옵션을 활성화 해보는 것을 추천드립니다.

https://sabarada.tistory.com/198

 

[gradle] gradle의 퍼포먼스를 늘릴 수 있는 유용한 정보 및 옵션들

요즘은 spring의 빌드 도구로 java, kotlin을 가리지 않고 gradle을 많이 사용합니다. 오늘은 이와 관련된 주제로 gradle의 퍼포먼스를 늘릴 수 있는 유용한 정보 및 옵션드에 대해서 알아보는 시간을 가

sabarada.tistory.com

 

그리고 실행 시간은...그냥 돈 좀 더 내고 코어 수를 늘리는게 제일 확실한 해결책입니다.

(구름 IDE 학생 요금제 쓰고 나서 내 삶이 달라졌다. 하지만 이젠 요금제 정책이 바껴서 사용할 수 없습니다)

 

조금만 규모가 커져도 구름 기본 컨테이너 성능은 답답해질것입니다. (JPA 강의 정도만 되도)

그냥 돈 좀 더 내고 코어 수를 늘리면 마음이 편해집니다.

 

지금은 goorm ide 가격정책이 바뀌어서 컨테이너의 코어 수에 따라 가격이 변경되는데

기본 컨테이너는 0.5core라고 되어 있는데 여기서 2core 정도로만 올려도 성능이 괜찮았습니다.

돈 좀 더 써도 되니까 빠른 빌드를 원한다면 성능을 올려 사용해보는게 좋을 것 같습니다.

1-3. 자동 완성이 안나와요

어쩔수 없습니다. 개같은 goorm ide

 

구글 들어가서 검색하고 패키지 구조 찾아서 직접 import 해야죠 뭐

"import AccessDeniedHandler" 처럼 import + 찾으려는 클래스 붙여서 검색하면 보통 바로 나옵니다.

그냥 github codespaces 쓰자

 

1-4 url 지맘대로 변경 이슈

온라인 IDE를 사용해 개발을 하면 우리가 만든 웹 어플리케이션 서버에 연결하기 위해선 localhost가 아닌

온라인 IDE에서 빌려준 컴퓨터의 주소로 연결해야 합니다.

그래서 대부분 IDE는 실행 url을 제공해주는데 구름 IDE는 이 url의 최상위 도메인이 가끔 지 맘대로 변합니다.

.site 랑 .io 둘 중에서 보통 변하는데 이게 가끔 문제가 될때가 있습니다.

 

보통 url을 변수명 등으로 직접 입력해야 하는 기능에서, 어제까지 멀쩡하던게 안되는 상황을 경험하게 됩니다.

이게 또 주소창에 쳐서 들어갈땐 알아서 지금 실행중인 곳으로 바꿔줘서 바뀐지도 모릅니다.

 

변수에 url을 직접 입력해 사용하는 상황(swagger, redirect, cors 설정 등등)에서 오류가 날때 url을 다시 확인해보는 습관을 들입시다.

1-5 그 외 구름 IDE를 사용하며 느낀점

설정파일 분리를 해놓으면 편합니다.

이건 Python 장고 프로젝트 진행할때도 마찬가지이다.

  • 휴가때 집에서 이어서 공부를 해야하거나(근데 휴가때 누가 공부를 해요)
  • 전역 후에도 이어서 할 프로젝트를 진행중이거나
  • 배포까지 염두에 둔 프로젝트를 진행하는 상황이면

집에서도 구름 IDE 쓰기 싫으면 설정파일 분리를 해두면 좋습니다.

 

아 그리고 구름은 고놈의 보안 정책 상 docker와 ssh 연결이 막혀있습니다.

배포를 염두에 둔 프로젝트를 진행할때 참고합시다.

참고 하라고 ㅋㅋ

 

 

사실 그냥 백엔드를 안하면 된다.

 

 

 

 

'군머' 카테고리의 다른 글

마음가짐  (4) 2024.06.21
마법의 소라고동님 군대에서 백엔드 공부를 해도 될까요?  (0) 2024.06.20

마법의 소라고동님 군대에서 백엔드 공부를 해도 될까요??
"안 돼"

안돼. 하지마.

 

라고 말하고 싶지만 기어코 군대에서 웹 개발을 공부하고 싶어서 특히 백엔드를 공부하고 싶어 이 글을 들어온 당신을 위해 써보겠습니다.

 

2022년 8월 22일, 가만히 있어도 땀이 뻘뻘 나던 한 여름에 머리를 빡빡 밀고 경남 진주로 향한 한 컴공생이 있었습니다.

그 컴공생은 꿈이 있었는데 바로 군 생활 1년 반을 그냥 허투루 날리지 않고 싶었다는 거였어요.

그래서 자기계발 여건이 비교적 보장된다는 공군에 지원했는데, 

집에서 가까운 자대를 고르다보니 수도권에 있는 소규모 부대에 가게 되었답니다!

(공군은 훈련소 성적으로 특기와 자대를 골라서 간다는 사실!)

 

근데 이런거 진짜 있긴 한가

요즘은 부대 안에 PC방이 있는 부대도 있다는 말을 들어서 기대를 하고 자대에 도착한 컴공생은 화들짝 놀랄 수 밖에 없었어요.

 

가장 비슷해보이는 사진을 들고 왔는데 사실 이 사진보다 더 낡고 안 좋았다.

 

작은 부대라 그런지 페이스북에서 나보던 사지방 시설을 보고, 여기서 공부를 할 수 있으려나 싶었답니다

 

당연히 그 컴공생은 저고요, 그래도 이미 도착한 자대를 바꿀 수는 없고 사지방 빼고는 나름 시설은 괜찮았답니다..? 나름?

그리고 사지방 시설이 안 좋아서 그런지 자리는 항상 넘쳐났고, 어떻게 잘만 하면 공부를 할 수 있겠다 싶어서 열심히 찾아봤습니다.

 

그리고 1년 반 동안 공부만 한거 치곤 초라하지만 그래도 군대에서 백준 플레티넘 찍기, 입대 전 하던 장고 프로젝트 리팩토링, JAVA, spring boot, JPA 공부 시작, 토이 프로젝트 개발 및 배포 정도의 결과를 얻어갔습니다.

 

그리고 그 과정에서 얻은 결과와 후기를 블로그에 남겨보려고 합니다.

 

사지방에서 백엔드 공부를 하고 싶어요.

챗지피티도 하지 말라고 한다.

 

사실 백엔드 공부를 그리 추천하고 싶지는 않습니다.  사실 군대에서 공부하기 편한 분야가 어디 있겠냐 싶지만

자격증 공부, 제 2 외국어, 하다못에 알고리즘이나 코딩 테스트 준비가 훨씬 쉽지, 웹개발을 공부하기엔 상당히 불편한 환경입니다.

 

대부분의 부대는 아직도 저희 부대처럼 저사양 컴퓨터 + 하모니카 OS 의 기막힌 조합으로 구성된 사지방을 사용할 것입니다.

이 환경에서 웹개발을 시도한다면,

  1. 웹페이지 4~5개만 켜도 멈춰버리는 기적의 컴퓨터 사양 (Figma나 유튜브라도 키면 2개만 넘어도 피를 토하며 멈춘다)
  2. 왜인지 사람이 몰리는 시간엔 로그인 조차 안되는 무친 OS
  3. 정신나간 국방부 사지방 유해 사이트 정책

등으로 안그래도 힘든 군생활동안 너덜너덜해진 멘탈이 산산조각날 것입니다.

 

특히 3번 유해사이트 정책이 사람을 진짜 힘들게 하는데, 사실 유해 사이트 한두번 들어 간다고 크게 문제가 되진 않습니다.

(후술할 이유 때문에 실수로 몇 번 들어가봤는데 1도 문제 없이 전역했습니다. 토토 같은 불법 사이트만 안들어가면 괜찮지 않을까요..?)

근데 들어가는 순간 경고가 뜨면서 실행중이던 모든 웹 페이지가 바로 닫힙니다.

 

당연히 저장을 하지 않은 내용이 있다면 전부 날아가고, 크롬 기능으로 복원을 시키면 유해 사이트도 같이 복원되서(이게 진짜 골때린다)

복원 → 강제 종료 → 복원 → 강제종료를 반복하는데 이게 진짜 사람을 개빡치게 미치게 합니다.

 

유해 사이트 탐지와 강제 종료에 약간 시간이 있어서 복원 되자마자 문제가 된 사이트를 빠르게 끄면 복구가 가능하긴 한데,

그마저도 결국 원래 켜놨던 웹 사이트로 리다이렉트 시켜주는 선에서 끝이라 온라인으로 작업하던건 저장 안하면 다 사라집니다.

 

그럼 유해 사이트를 안들어 가면 되는거 아닌가요..?

그러기엔 진짜 상상도 못하는 것들이 유해 사이트로 지정이 되어 있습니다.

예를 들면

  • 네이버 스포츠는 되지만 상세 종목 채널에서 e스포츠를 누르면 거긴 유해 사이트
  • 주소창에 youtube.com 을 쳐서 나오는건 유해 사이트. (근데 구글에 "유튜브" 검색해서 들어가는건 됨),
  • 티스토리나 VELOG가 아닌 대부분의 블로그 등등

사지방의 개발 환경은 로컬 개발 환경과 다르기 때문에

강의 보고 코드를 그대로 따라치기만 했는데도 정말 수많은 원인을 알 수 없는 오류를 맞이할 것입니다.

 

그럴때 당연히 구글에 에러 메세지와 원인을 검색해보고

위의 사진 맨 위에 나온 것 같은 medium 도메인의 블로그를 아무 생각 없이 누르면..?

 

강제종료 → 복원 → 강제 종료 → 복원 → 강제종료 끼아아아아악

 

그리고 잼민이 시절 컴퓨터실에 몰래 깔아놨던 마인크래프트 다음 주 수업시간에 다시 오면 다 사라지듯

컴퓨터 끄는 순간 저장한 모든 정보가 사라집니다. 애초에 하모니카 OS 컴퓨터는 파일 다운로드도 대부분 막혀있습니다.

 

사실 이 부분은 우회하는 방법이 없는 건 아니지만 불법이라고 알고 있으니 시도하지 않는 것을 추천드립니다.

애초에 보안 규정이라고 만들어 놓은건데 그걸 우회하면 당연히 문제가 생기지 않을까?

혹시 시도해보고 싶은 용감한 사람을 위해 정리된 글을 가져와 봤다.

https://uzzam.dev/15

 

군대 사지방에서 개발하는 방법들

이 글은 2020년 6월 20일에 첫 작성되었습니다. (https://github.com/shs395/shs395.github.io/blob/master/content/army/how-to-development-in-army/index.md) 사지방 컴퓨터의 운영체제가 하모니카OS가 아닌 win7인 경우에 해당

uzzam.dev

 

그렇다면 소스코드는 깃허브에 저장한다고 쳐도 IDE를 저장하는건 불가능합니다.

하지만 이런 우리에게도 온라인 IDE라는 방법이 있습니다.

그래서 온라인 IDE가 뭔데요

온라인 IDE는 된다신다 만세

 

별도의 구축 없이 웹 브라우저에서 IDE 기능을 가진 통합 개발 환경을 의미합니다. 

즉 웹에서 돌아가는 온라인 코드 편집, 실행기 라고 생각하면 될 것 같습니다.

 

우리는 다음과 같은 조건을 만족하는 온라인 IDE를 찾아야하는데

  1. 가격
  2. 성능
  3. 사용 난이도

문제는 이걸 만족하는 온라인 IDE를 찾기 매우 힘듭니다.. (특히 1번과 2번 조건이 양립하는게 거의 불가능)

그리고 제일 큰 문제는 노트북 무게가 그램 단위로 나오는 세상에서 온라인 IDE로 개발을 하는 사람이 세상에 몇이나 있을까요..?

개발 환경에 대한 리소스가 턱없이 부족합니다.

검색을 해봐도 자세한 설명이 있는 글들이 거의 없습니다. 이 글을 쓰기로 마음 먹은 이유이기도 합니다.

 

백엔드 개발을 한다는 가정하에 가장 중요한건 웹 서버를 와 DB를 돌릴 수 있는 서버 리소스가 제공되는지 여부인 것 같습니다.

몇몇 온라인 IDE는 단순히 실행 결과 정도만 보여주고 웹 개발 프레임 워크나 DB를 설치하고 사용할 리소스를 제공하지 않습니다.

 

그래서 백엔드 웹 개발을 공부한다는 기준으로 후보군은 이 정도인 것 같습니다.

  1. 구름 ide
  2. github codespace
  3. code-server + GCP
  4. code-anywhere

1. 구름 IDE

https://www.goorm.io

 

구름HOME

구름은 클라우드 기술을 이용하여 누구나 코딩을 배우고, 실력을 평가하고, 소프트웨어를 개발할 수 있는 클라우드 소프트웨어 생태계입니다.

www.goorm.io

 

결론부터 이야기하면 제가 군생활동안 사용하기로 결정했던 웹 IDE입니다.

ubuntu os의 aws ec2와 도커 기반 컨테이너를 이용해 만들어진 서비스입니다.

 

가장 큰 장점은 한글 지원 + 무료 이용 가능, 꽤나 넉넉한 저장공간, 웹 기반 프레임워크 기본 템플릿 제공 등이 있습니다.

 

또한 예전엔 학생 요금제를 사용하면 월 6~7천원대에 24시간 컨테이너 실행이 가능했고, cpu, ram 사양도 쓸만할 정도 (aws freetier 이상)로 올라가서 웹 서버 배포까지 가능했다는 매우 큰 장점이 있었지만 24년 4월부로 요금제가 바뀌고 불가능해졌습니다.

(불가능은 아니지만 에전과 같은 사양으로 24시간 돌리면 가격이 2배 이상으로 나올듯 합니다)

 

그리고 한글 지원이 된다는 점이, 안 그래도 적은 리소스의 웹 IDE 생태계에서 꽤나 이점이라고 생각합니다.

 

하지만 단점도 뚜렷한데

  1. 기본 제공 템플릿 버전 업데이트가 매우매우 느림 (spring boot 2.0.1 제공중 ㅋㅋ)
  2. 도커 기반 환경이라 프로젝트에서 docker 설치 밑 사용이 보안상 이슈로 제한됨
  3. 가격 정책 변경으로 예전 처럼 가성비 있게 사용할 수 없음
  4. IDE 자체의 기능이 적음
    1. vs code 같은 외부 extension 설치 안됨
    2. 자동완성 기능 매후 미흡 (java를 쓴다면 패키지 직접 찾아서 import 써줘야함)
    3. 프로젝트 실행하려면 터미널 명령어로 실행시켜야함(gradle build && java -jar ..) -> 빌드에 시간 오래 걸림

24년 5월까지 군생활을 했던 사람으로서 가성비에 한글 지원이라는 두 가지 장점을 보고 사용했지만,

기존에 웹 개발을 공부했던 경험이 있다면 (특히 자바, spring boot) 후술할 다른 IDE를 먼저 시도해 보는 것도 좋은 것 같습니다.

 

구름 IDE 사용방법과 개발환경 설정은 쉬운 편이고, python django, fastApi, flask 등을 사용하거나(빌드 시간 짧음),

새로운 프레임워크에 대해 공부해보고 싶다면 옛날 버전이긴 하지만 템플릿을 제공해주는 구름 IDE도 나쁘지 않을 수 있다고 생각합니다.

2. Github Codespace

https://github.com/features/codespaces

 

Secure development made simple

GitHub Codespaces gets you up and coding faster with fully configured, secure cloud development environments native to GitHub.

github.com

 

사실 제대로 안 사용해봐서 잘 모른다.

처음에 Goorm IDE를 쓰며 JPA 강의를 듣다가 import 문 직접 찾아서 입력하는거에 지쳐서

다른 IDE를 찾아보던 중 잠깐 사용해본 IDE입니다.

 

일단 에디터가 vs code 기반인 건 큰 장점입니다.

잠깐 쓰는데도 자동완성, 빠른 빌드&실행 툴 덕분에 마음이 편안했습니다.

또한 spring boot를 포함한 대부분의 웹 프레임워크 기본 템플릿을 제공하며,

기존에 진행하던 프로젝트가 있다면 github 리포지토리에 연결해 이어서 작업할 수 있습니다. (사실 이건 구름도 가능)

 

단점은 역시 가격이 아닐까 싶습니다.

https://docs.github.com/ko/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces

 

GitHub Codespaces 청구 관련 정보 - GitHub Docs

GitHub Codespaces을(를) 사용하는 비용과 GitHub 개인 계정 포함된 월별 사용량 할당량에 대해 알아봅니다.

docs.github.com

 

개인 계정의 경우 매월 120시간의 무료 사용량이 존재하지만 1 코어 기준으로 120시간이므로

2코어, 4코어 Vm을 생성한다면 60시간, 30시간만 무료로 사용 가능합니다.

 

2코어 기준 하루 3시간 사용한다고 하면 20일간 사용이 가능합니다만,

주말에 조금 더 오래 공부 한다고 치면 사실상 15일 정도만 무료로 사용이 가능합니다.

 

그 이후엔 2코어 기준으로 시간당 0.18$의 사용료를 받습니다. 120시간을 더 사용한다는 가정하에 한달 21.6달러가 나오는데

물론 공부를 그렇게 열심히 하지 않을 사람이라면 저 정도는 아니겠지만, 부담없이 마음껏 쓸만한 가격은 아니라고 생각합니다.

학생 계정 인증을 받는다면 180시간까지 사용이 가능하니 휴학후 입대하신 분들은 참고해보시면 좋을 듯 합니다.

학생 계정 인증도 받고 평일 하루 2시간, 주말 6시간씩 사용하면 2코어 컴퓨터 기준 4주간 무료로 사용이 가능하다는 계산이 나옵니다.

이 정도면 사용해볼만 한 것 같습니다.

3. Code Server + GCP(AWS)

Code Server 역시 vs code 환경에서 돌아가는 웹 IDE입니다.

하지만 code server는 리소스를 직접 제공해주지 않으므로 VM을 직접 세팅해야 합니다.

 

직접 gcp나 aws ec2등의 클라우드 컴퓨팅 서비스를 이용해 리소스를 빌리고, 개발환경을 세팅한 후 code server에 연결해야 합니다.

 

세팅하다가 막혀서 때려 치웠는데, aws나 gcp에 대한 기본 지식이 있는 사람이라면 괜찮은 선택지라고 생각합니다.

(근데 이상하게 구글링해보면 aws ec2보다는 gcp와 더 많이 쓰는데 그 이유는 잘 모르겠습니다.)

 

이건 직접 써보진 않았어서 다른 소개글을 가져와봤습니다.

물론 react나 vue를 이용한 개발을 다루고 있지만, gcp 환경 설정, 개발 환경 세팅 과정에서 참고하면 좋을 것 같습니다.

https://velog.io/@2wndrhs/code-server로-사지방에서-코딩하기-1-GCP-인스턴스-생성

 

code-server로 사지방에서 코딩하기 - 1 (GCP 인스턴스 생성)

code-server로 사지방에서 코딩하기

velog.io

물론 사지방에서 spring boot를 공부하고 싶다면 프로젝트를 서버에 설치해 시작하는게 가능할 지 모르겠습니다.

기존에 작업하던 프로젝트가 존재한다면 github에 저장된거 clone 해오면 되겠지만, 새로 시작하고 싶다면..

 

구글링해봐도 대부분 자료들이 이미 만들어진 spring boot 프로젝트를 vm에서 배포하는 방법만 다루고 있습니다.

spring boot 프로젝트 생성을 찾아보면 IntelliJ 기반으로 start.spring.io에 들어가서 다운 받아 실행 하는 글이 99프로이지만,

우리는 그럴 수가 없습니다.

 

터미널로 java , DB 설치 후 밑에 글을 참고하면 될지도..? 혹은 wget을 잘 이용하면 가능하지 않을까 싶습니다.

(안해봐서 모르겠지만 응원합니다.)

https://blog.hyunsub.kim/Spring/Spring-with-Terminal/

 

Terminal로 Spring Boot 다루기

IntelliJ나 eclipse와 같은 IDE의 도움을 받지 않고 Spring을 빌드, 실행, 테스트 하는 방법을 알아보겠습니다. Maven과 Gradle 모두 다루고 있습니다.

blog.hyunsub.kim

 

4. Code Anywhere

https://codeanywhere.com

 

Cloud IDE · Online Code Editor · Codeanywhere

Save time by deploying a development environment in seconds. Collaborate, code, learn, build, and run your projects directly from your browser.

codeanywhere.com

 

이건 블로그 쓰려고 찾아보다 발견했습니다.

1년 기준 한달 7.5달러에 150시간 이용 가능하다는데, 그냥 code space 쓸 것 같긴 합니다만

관심이 있다면 한 번 찾아보면 좋을 듯 합니다.

 

마무리하며

사실 군대에선 백엔드가 아니라 알고리즘, CS지식 이론 공부, 프론트엔드 개발을 공부하는게 정신건강에 크게 이로울거라 생각합니다.

하지만 백엔드를 공부하고 싶다면, 혹은 백엔드 공부를 하다가 입대를 했다면 군대라서 힘들겠지.. 라는 마음만 먹기보단 시도해보는 것도 추천합니다. (아무것도 안하고 누워서 롤토체스나 하는 것 보다 백배 천배 나을겁니다.)

 

그리고 오히려 IDE의 기능에 익숙해져 공부하지 못하던 터미널 사용법, 리눅스 OS, 듣도보도 못한 오류 해결하기 등에 대한 능력을 키울 수 있는 기회이기도 합니다.

 

다음 글에선 구름 IDE를 기준으로 웹 IDE를 이용한 개발 환경 설정 팁 및 참고 하면 좋은 내용들에 대해 써보겠습니다.

 

<참고 글>

https://neurowhai.tistory.com/192

 

사이버 지식 정보방(사지방) 개발 환경

안녕하세요! 오늘은 제가 사지방에서 개발을 하기 위해 어떤 도구들을 이용했는지 적어보고자 합니다. 다만 부대마다 사지방 환경이 다르므로 적당히 걸러서 들으시면 되겠습니다. 또한 개발이

neurowhai.tistory.com

 

'군머' 카테고리의 다른 글

마음가짐  (4) 2024.06.21
군대에서 백엔드 공부할 때 참고할 것  (0) 2024.06.20

+ Recent posts