profile image

L o a d i n g . . .

728x90

챕터 1은 데이터를 다루는 애플리케이션을 신뢰할 수 있고 확장 가능하며 유지 보수할 수 있는 형태로 개발하는 방법을 살펴보는 큰 그림을 제시합니다. 저자는 신뢰성, 확장성 및 유지 보수성이라는 세 가지 특성에 중점을 두어 애플리케이션을 개발하는 방법을 소개합니다.

 

신뢰성(Reliability)

신뢰성이 높은 애플리케이션은 다음과 같은 특징을 갖습니다:

  • 애플리케이션은 예상한 대로 동작합니다.
  • 시스템은 예상치 못한 사용자 동작에도 견딜 수 있습니다.
  • 예상된 부하 상황에서도 애플리케이션은 정상적으로 동작합니다.
  • 권한이 없는 요청을 차단할 수 있습니다.

저자는 애플리케이션의 신뢰성을 저하시킬 수 있는 두 가지 요소인 failure와 fault를 설명합니다. Failure는 시스템이 사용자 요청을 처리할 수 없는 상태를 의미합니다. Fault는 failure보다 범위가 작으며, 시스템 일부 기능이 스펙과 다르게 동작하는 상태를 의미합니다. 모든 fault를 방지하는 것은 불가능하며, 가능하더라도 비용 대비 이점이 적을 수 있습니다. 그러므로, 방지하기 어려운 fault는 별도의 대응 매뉴얼을 마련해 두는 것이 신뢰성을 유지하는 하나의 방법이 될 수 있습니다.

Hardware fail

시스템에 영향을 미치는 failure 또는 fault는 하드웨어 또는 소프트웨어에서 발생할 수 있습니다. 먼저 하드웨어 부분을 살펴보면, 하드웨어의 장애 주기를 측정하는 지표로는 MTBF(Mean Time Between Failure)가 있습니다. Samsung SSD와 관련된 문서에서는 MTBF를 "SSD와 관련된 작업 중 발생한 실패 사이의 예상된 간격"으로 정의하고 있습니다. Samsung SSD의 MTBF는 1,500,000시간으로, 대략 171년에 한 번의 오류가 발생할 가능성이 있습니다. 하지만, 만약 200개의 Samsung SSD를 사용한다면, 확률적으로 1년에 한 번의 오류가 발생할 가능성이 있습니다. 데이터 센터에서는 이보다 훨씬 많은 하드웨어를 사용하므로, 예를 들어 100,000개의 Samsung SSD를 사용한다면, 거의 매일 Samsung SSD와 관련된 오류가 발생할 수 있습니다. 따라서, 개별 하드웨어의 MTBF가 길다 하더라도 항상 hardware fault가 발생할 가능성이 있음을 인지하고 이에 대처할 수 있는 방법을 마련해야 합니다. 하드웨어 오류를 대처하는 방법으로는 RAID 구성, 전력 공급장치의 이중화, 데이터 센터의 백업 파워(배터리) 등이 있습니다.

Samsung SSD MTBF

Software fail 

하드웨어로 인한 오류는 상대적으로 개별 하드웨어 단위로 영향을 끼치는 경우가 많지만, 소프트웨어로 인한 오류는 해당 소프트웨어를 사용하는 다양한 시스템에 영향을 끼칠 가능성이 높기 때문에 하드웨어에 비해 광범위한 영향력을 가집니다. 이와 관련하여 2012년 6월 Leap Second 추가로 발생한 Linux 오류가 대표적인 예입니다. 해당 오류는 Reddit과 같은 대형 웹 서비스를 다운시키는 결과를 초래했습니다. Software 오류는 다양한 edge case가 존재하기 때문에, 하드웨어 오류와 마찬가지로 100% 방지하는 것은 불가능합니다. 따라서, 오류 발생 가능성을 인정하고 이를 어떻게 식별하고 대처할 것인지를 결정하는 것이 중요합니다.

Leap second 추가로 인해 발생한 java 오류관련 대화

Human error 

소프트웨어 세계에서 대다수 오류의 원인은 인적 오류입니다. 코드를 잘못 작성하거나 설정을 잘못한 경우 등 여러 방법으로 인해 인적 오류가 발생할 수 있습니다. 누구나 실수를 할 수 있기 때문에 인적 오류를 최소화할 수 있는 프로세스가 필요합니다. Pull request 시에는 동료들이 코드를 확인하고, 자동화된 테스트를 통해 변경으로 인한 버그를 미리 발견할 수 있습니다. 이러한 프로세스가 있더라도 인적 오류를 완전히 방지할 수 없으므로, 인적 오류에 의해 실패가 발생한 경우 이를 어떻게 감지하고 대처할 것인지(rollback plan 등)를 결정해야 합니다.

 

Scalabillity 

다음으로는 확장성입니다. 애플리케이션이 초창기에는 신뢰성이 있었지만 시간이 지남에 따라 유저와 부하가 증가하면서 신뢰할 수 없는 상태가 될 수 있습니다. 따라서, 애플리케이션을 개발할 때는 확장성을 고려하여 개발해야 합니다. 하지만 애플리케이션의 확장성은 너무 포괄적이고 추상적인 의미를 가지기 때문에, 범위를 좁힐 필요가 있습니다. 예를 들어, "시스템 A는 확장성이 있다"라는 표현보다는 "시스템 A는 B 부하가 증가하는 상황에서 C 방법을 통해 확장할 수 있다"라고 표현하는 것이 해당 시스템의 확장성을 더욱 명확하게 표현할 수 있습니다.

Load 

확장성 있는 시스템을 설계할 때는 해당 시스템이 어떤 유형의 부하에 대비해야 하는지 고려해야 합니다. 예를 들어, 웹 서버의 경우 TPS(Traffic Per Second) 부하가 증가하는 상황에서 어떻게 확장할 수 있는지를 고민해 볼 수 있습니다. 데이터베이스의 경우 WPS(Write Per Second) 또는 RPS(Read Per Second)에 따라 어떤 데이터베이스를 사용해야 할지를 고민해 볼 수 있습니다.

부하와 관련해서 자주 소개되는 트위터의 사례를 살펴보겠습니다. 트위터는 유저가 게시물을 업로드하고 팔로잉하는 유저의 게시물을 타임라인을 통해 볼 수 있는 서비스를 제공합니다. 초창기 트위터 모델에서는 유저가 타임라인을 확인하기 위해서 서버에서는 다음과 같은 데이터베이스 쿼리를 사용했습니다.

SELECT tweets.*, users.*
FROM tweets
JOIN users   ON tweets.sender_id    = users.id // 자신의 게시물 
JOIN follows ON follows.followee_id = users.id // following 하는 유저의 게시물 
WHERE follows.follower_id = current_user

트위터는 요청의 대부분이 읽기임을 발견했고 이에 맞춰 쓰기 성능을 조금 희생하여 읽기 성능을 개선하는 방법을 채택하였습니다. 각각의 유저는 확인 가능한 게시물을 저장하는 일종의 저장소를 할당하고, 새로운 게시물이 생성되면 관련된 유저의 저장소에 게시물을 push 하는 fan-out 방법을 사용했습니다. 하지만 팔로워의 수가 많은 유저의 경우에는 이 방법이 문제가 되었는데, 팔로워 수가 많은 유저가 게시물을 작성하면 팔로워 모두에게 데이터를 전송해야 했기 때문에 시스템에 과도한 부하를 유발했습니다. 이 문제를 해결하기 위해 트위터는 hybrid 방법을 도입하였습니다. 게시물이 작성될 때 팔로워의 저장소에 데이터를 push 하는 fan-out은 유지하되, 특정한 수 이상의 팔로워를 가진 슈퍼 유저가 게시물을 작성하면 팔로워들에게 데이터를 push 하지 않고 데이터베이스에 저장합니다. 그리고 해당 슈퍼 유저의 게시물은 일반 유저가 타임라인을 조회할 때 별도의 로직을 통해 읽어오도록 하였습니다. 

Scaling 

시스템을 확장하는 방법에는 고성능 장비로 교체하는 vertical scaling과 다수의 컴퓨팅 리소스를 활용하여 확장하는 horizontal scaling이 있습니다. Vertical scaling은 더 좋고 비싼 컴퓨팅 리소스를 사용하여 확장하는 방법이고, horizontal scaling은 더 저렴한 리소스를 여러 대 사용하여 확장하는 방법입니다. Stateless application(웹 서버 등)의 경우에는 상태를 별도로 관리하지 않기 때문에 horizontal scaling이 이점이 큽니다. 하지만 stateful application(데이터베이스 등)의 경우에는 상태를 관리하기 때문에 horizontal scaling을 도입했을 때 성능이 저조하거나 오히려 비용이 높을 수 있습니다. 따라서 scaling을 고려할 때, 확장하고자 하는 애플리케이션의 특성을 반드시 고려해야 합니다.

 

Maintainabillity

유지보수는 미래의 개발자뿐 아니라 나 자신을 위해서라도 중요합니다. 처음 코드를 작성할 때는 이해하기 쉬워도 시간이 지나면 자신의 코드조차 이해하기 어려운 경우가 많습니다. 따라서 유지보수성을 고려하는 게 다른 개발자뿐 아니라 자신에게도 도움이 됩니다. 유지보수성이 좋은 애플리케이션은 다음과 같은 특성을 가지고 있습니다:

  • Operability
    • 동작하는 시스템을 검사할 수 있는 도구가 있습니다.
    • 다른 표준 도구와 쉽게 통합됩니다.
    • 자동화
    • 특정 머신에 의존하지 않습니다.
    • 문서화가 잘 되어 있습니다.
    • 오류 발생 시 self-healing이 가능합니다.
    • 개발자가 예상한 범위 내에서 동작합니다.
  • Simplicity
    • 불필요한 복잡성이 존재하지 않습니다.
    • 적절한 추상화를 사용하여 복잡성을 낮춥니다.
  • Evolvability
    • 새로운 기능을 추가하거나 기존 기능을 수정하기 쉽습니다.
    • 테스트 주도 개발(Test-driven development)과 리팩토링(refactoring)을 활용할 수 있습니다.

 

마무리 

이번 챕터에서는 데이터를 다루는 애플리케이션을 개발하는 방법에 대해 살펴보았습니다. Reliable, scalable, maintainable 한 애플리케이션의 특성과 이러한 애플리케이션을 만들 때 사용되는 best practice를 배운다면 더 견고한 애플리케이션을 개발할 수 있는 훌륭한 개발자가 될 것 같습니다. 

728x90
복사했습니다!