Category: 개발

Blender에서 USD 임포트 속도 저하의 특이 사례

2024-12-22

개발자가 Blender에서 USD 장면을 임포트할 때 예상치 못한 속도 저하 문제를 발견했습니다. 프로파일링 결과, 병목 현상이 Blender의 내부 ID 정렬 함수인 `id_sort_by_name`에 있음을 알게 되었습니다. 이 함수는 O(N)의 복잡도를 가질 것으로 예상되었지만, USD 파일의 명명 체계 때문에 O(N²)로 저하되었습니다. 명명 규칙을 변경하고 정렬 알고리즘을 최적화함으로써 개발자는 작은 파일의 임포트 시간을 4분 40초에서 8초로 단축했습니다. 하지만 근본적인 문제는 Blender가 정렬된 ID를 필요로 한다는 점이며, 연결 리스트를 트라이 또는 해시 테이블로 대체하는 것이 제안되었습니다. 이 최적화는 성능 튜닝에서 흔히 발생하는 문제, 즉 예상치 못한 복잡성을 식별하고 해결하는 것을 보여줍니다.

개발

cqd: 객체 속성을 다채롭게 보여주는 Python 유틸리티

2024-12-22

cqd는 객체 속성을 다채롭게 시각화하여 개발 및 디버깅 중 객체 검사를 간소화하는 경량 Python 유틸리티입니다. 속성은 색상으로 구분됩니다. dunder 메서드(파란색), 보호된 속성(노란색), 공용 속성/메서드(녹색)입니다. 예를 들어, Hugging Face 토크나이저의 속성을 쉽게 확인하는 데 유용합니다. 설치는 `pip install cqd`로 간편하게 할 수 있습니다. 사용하려면 `cqd` 함수를 가져와 `cqd(your_object)`를 호출합니다.

개발 객체 속성

Java JEP 483: 사전 클래스 로딩 및 링크를 통한 시작 시간 단축

2024-12-22

JEP 483은 HotSpot JVM 시작 시 애플리케이션 클래스를 미리 로딩 및 링크하여 Java 애플리케이션의 시작 시간을 크게 줄입니다. 애플리케이션의 단일 실행을 모니터링하고 모든 클래스의 로드 및 링크된 형태를 캐시에 저장하여 후속 실행에서 재사용함으로써 이를 달성합니다. 이 기능은 코드 변경이 필요 없으며 Spring PetClinic과 같은 대규모 서버 애플리케이션에서 상당한 속도 향상을 제공합니다(시작 시간 42% 단축). 현재는 2단계 프로세스이지만 향후 버전에서는 캐시 생성이 1단계로 간소화되고 더 유연한 교육 실행 구성이 가능해집니다.

개발

Meta의 대규모 Java에서 Kotlin으로의 마이그레이션: 수백만 줄의 코드 극복

2024-12-22

Meta는 방대한 Android 코드베이스를 Java에서 Kotlin으로 마이그레이션하기 위해 수년간 노력해 왔습니다. 이 게시물에서는 Meta가 Kotlinator라는 자동화 도구를 구축하여 빌드 속도 저하 및 부족한 린터와 같은 과제를 극복하고 코드의 절반 이상을 성공적으로 변환한 방법을 자세히 설명합니다. Kotlinator는 사전 처리, 헤드리스 J2K 변환, 사후 처리, 오류 수정 등 여러 단계로 구성됩니다. Meta는 JetBrains와도 협력하여 J2K를 개선하고 커뮤니티 협업을 촉진하기 위해 프로세스의 일부를 오픈소스로 공개했습니다. 이 기사에서는 null 안전성 처리 및 변환 중 발생한 다양한 코드 문제와 해결책에 대해 중점적으로 설명합니다.

담벼락 너머 대화가 프로그래머로서의 인생 방향을 바꾸다

2024-12-21

1983년, 대형 방위산업체에서 일하던 프로그래머는 화학 박사 학위를 취득할 계획이었습니다. 우연히 옆 사무실 "마이크로컴퓨터 그룹" (기술에 조예가 깊은 관리자가 있던 부서) 매니저와 담벼락 너머 대화를 나눈 것이 계기가 되어 Apple II에 관한 회의에 초대되었습니다. 회의에서 회사 사장이 집에서 이메일을 읽을 수 있도록 1주일 안에 6502 어셈블리어로 VT-100 터미널 에뮬레이터를 만들라는 긴급한 임무를 맡게 됩니다. 이 경험은 그의 커리어 방향 전환, 마이크로컴퓨터 그룹 합류, 회사 내 유일한 PC 프로그래머가 되는 것, 그리고 자신의 회사를 설립하는 것으로 이어졌습니다. 여러 해가 지난 후 그는 우연한 만남과 인간관계가 자신의 인생을 얼마나 크게 바꾸었는지 회고했습니다.

개발 커리어 기회

소프트웨어 디자인 철학: 복잡성 제어하기

2024-12-21

이 글은 『소프트웨어 디자인 철학』에서 제시하는 세 가지 핵심 개념을 요약합니다. 복잡성에 대한 제로 톨러런스, 더 작은 구성 요소가 항상 더 나은 모듈성으로 이어진다는 오해, 그리고 예외 처리에 내재된 복잡성입니다. 저자는 복잡성이 단일 오류로 인해 발생하는 것이 아니라 시간이 지남에 따라 누적된다고 주장합니다. 주문 처리 시스템과 사용자 등록의 예는 중복 코드를 피하고 구성 요소 크기와 모듈성 사이의 적절한 균형을 찾는 방법을 보여줍니다. 또한 이 글에서는 예외 처리의 복잡성을 줄이기 위한 세 가지 기법, 즉 오류 제거, 예외 마스킹, 예외 집계에 대해 자세히 설명하며, 파일 처리를 예로 제시합니다. 이 책은 궁극적으로 소프트웨어 디자인에서 복잡성을 지속적으로 단순화하는 중요성을 강조합니다.

열거형 배열: 효율적인 데이터 처리를 위한 새로운 데이터 구조

2024-12-21

이 글에서는 일반적인 "구조체 배열"(SoA)과 유사하지만 핵심에 열거형을 사용하는 "열거형 배열"(EoA)이라는 데이터 구조를 소개합니다. EoA는 여러 열거형 값을 배열로 묶고, 단일 태그로 배열의 유형을 식별하여 메모리 사용량과 분기 예측 오버헤드를 줄입니다. 이를 통해 데이터 처리 효율이 향상되고, 특히 SIMD 최적화에 효과적입니다. 이 글에서는 데이터베이스 시스템 TigerBeetle을 예로 들어 EoA가 배치 처리에서 어떻게 효과를 발휘하고, 제어 플레인과 데이터 플레인을 효율적으로 분리하여 성능을 크게 향상시키는지 설명합니다.

Yakari: 복잡한 CLI를 간소화하는 대화형 명령어 생성 도구

2024-12-21

Yakari는 복잡한 명령줄 인터페이스를 간소화하도록 설계된 대화형 명령어 생성 도구입니다. 단계별로 명령어 생성을 안내하여 복잡한 구문을 암기할 필요가 없습니다. 다양한 인수 유형을 지원하고, 컨텍스트 도움말과 명령어 기록을 제공하여 CLI의 사용 편의성을 크게 향상시킵니다. 사용자는 간단한 바로 가기 키로 명령어를 생성하고 실행할 수 있으므로 복잡한 명령어도 쉽게 사용할 수 있습니다.

Go 이터레이터: 페이징 API 효율적으로 처리하기

2024-12-21

이 글에서는 Go 1.23에 도입된 이터레이터 기능을 사용하여 페이징 API를 효율적으로 처리하는 방법을 보여줍니다. GitHub API를 예시로 사용하여 페이징 로직을 추상화하고 코드를 더욱 읽기 쉽고 재사용 가능하게 만드는 사용자 정의 이터레이터를 만드는 방법을 설명합니다. 이터레이터의 구현과 테스트 방법, API 호출 모킹, 풀 이터레이터를 사용한 테스트 등을 중점적으로 다루며, 이터레이터가 예상대로 결과를 반환하는지 확인합니다. 이터레이터를 사용하면 개발자는 페이징 로직과 비즈니스 로직을 분리하여 코드의 유지보수성과 가독성을 높일 수 있습니다.

지속적 전달 파이프라인 선언: 더 나은 소프트웨어 제공 구축

2024-12-21

현대 소프트웨어 팀은 지속적 전달 파이프라인 관리를 위한 더 나은 도구가 절실히 필요합니다. 현재 CD 파이프라인 생태계는 단편적이고, 경직되어 있으며, 비효율적입니다. 이 선언서는 코드 우선, 개발자 친화적인 파이프라인을 주장하며, 현대 엔지니어링 워크플로의 복잡성을 처리하도록 설계되었습니다. 단일 정보 출처, 재사용 가능하고 형식이 안전한 구성 요소, 동적이고 유연한 파이프라인, 투명하고 시각적인 디버깅, 변화와 빠른 피드백 루프를 처리하기 위한 메커니즘을 강조하며, 궁극적으로 효율성을 높이고 제공 속도를 높이는 것을 목표로 합니다.

Rivet: Actor 모델을 사용한 실시간 애플리케이션 실행 및 확장

2024-12-21

Rivet은 Actor 모델을 사용하여 실시간 애플리케이션을 구축하고 확장하기 위한 플랫폼입니다. 내장된 RPC, 상태, 이벤트 기능을 통해 최신 애플리케이션 개발을 간소화합니다. Rivet은 자동 확장, 에지 네트워크 배포를 지원하며, 내장 모니터링 및 데이터 로컬라이제이션 기능을 제공합니다. Rust, FoundationDB, V8 isolates, Deno 런타임으로 구동되어 성능과 효율성을 보장합니다. Rivet은 협업 애플리케이션, 로컬 우선 애플리케이션, AI 에이전트, 게임 서버 등에 적합합니다.

SingleFile: 전체 웹페이지를 단일 HTML 파일로 저장

2024-12-21

SingleFile은 전체 웹페이지를 단일 HTML 파일로 저장하는 강력한 웹 확장 프로그램 및 CLI 도구입니다. Chrome, Firefox, Edge 등을 지원하며 편리한 페이지 저장, 여러 탭 처리, 주석 기능 등을 제공하며 저장된 페이지를 Google Drive 또는 GitHub에 업로드할 수도 있습니다. 바로 가기 키와 설정을 사용자 지정하여 자신의 요구 사항에 맞게 조정할 수 있습니다.

효율적인 독일어 학습: Anki가 답일까요?

2024-12-21

독일에서 8년 가까이 살았지만 여전히 독일어를 못하는 엔지니어가 Anki를 사용하여 독일어 학습 경험을 공유합니다. 매일 10개의 새로운 단어를 학습하여 1년 안에 C1 수준에 도달하는 것을 목표로 Anki의 간격 반복 기술을 활용합니다. 빈도 순으로 정렬된 Anki 덱을 선택하고 직접 오디오 발음을 추가했습니다. 저자는 독자들에게 Anki 사용 경험과 독일어 학습 팁을 공유해 줄 것을 요청합니다.

C 문자열 버그와 작별: 더 안전한 문자열 처리 방식

2024-12-21

C 문자열의 버그와 보안 문제에 지치셨나요? 이 글에서는 데이터 포인터와 길이를 포함하는 사용자 정의 문자열 구조체 `struct str`을 통해 널 종료로 인한 위험을 피하는 현명한 대안을 소개합니다. 베어메탈 환경에서 6개월간의 경험을 통해 이 방식이 버퍼 오버플로우와 같은 오류를 효과적으로 방지하는 것이 입증되었습니다. 매크로 `STR` 사용이 다소 장황하지만, 안전성 향상과 가독성 향상이 이를 훨씬 상쇄합니다. 컴파일러 최적화 덕분에 성능 저하도 무시할 수준입니다. 코드 보안을 우선시하는 개발자에게 새로운 접근 방식이 될 것입니다.

AI 코드 리뷰 봇이 조용해진 방법

2024-12-21

Greptile의 AI 코드 리뷰 봇은 처음에 과도한 코멘트 생성으로 비판을 받았습니다. 이 문제를 해결하기 위해 프롬프트 엔지니어링과 LLM의 자체 평가를 시도했지만, 이러한 방법은 효과적이지 않았습니다. 획기적인 돌파구는 이전 코멘트를 벡터화하고 벡터 데이터베이스에서 클러스터링하여 이전에 거부된 코멘트와 유사한 새로운 코멘트를 필터링하는 것이었습니다. 이 접근 방식을 통해 개발자의 처리율이 19%에서 55% 이상으로 증가하여 LLM 노이즈가 크게 감소했습니다.

개발 코드 리뷰

자막 편집기 Aegisub 3.4.0 출시!

2024-12-21

Aegisub 3.4.0이 출시되었습니다! 무료이며, 크로스 플랫폼, 오픈소스 자막 편집기로, 자막과 오디오 동기화를 빠르고 쉽게 할 수 있습니다. 강력한 스타일링 도구와 실시간 비디오 미리보기 기능을 갖추고 있습니다. 초보자부터 전문가까지 누구나 사용할 수 있습니다.

Raft 구현: 분산 합의에 대한 심층 분석

2024-12-21

이 글은 분산 합의 알고리즘 Raft와 Go를 사용한 구현에 대한 시리즈의 첫 번째 게시글입니다. Raft는 여러 서버에 걸쳐 결정론적 상태 머신을 복제하는 문제를 해결하여 서버 오류가 발생하더라도 서비스 가용성을 보장합니다. 이 게시글에서는 상태 머신, 로그, 합의 모듈, 리더/팔로워 역할, 클라이언트 상호 작용 등 Raft의 핵심 구성 요소를 소개합니다. 또한 Raft의 내결함성, CAP 정리, Go를 구현 언어로 선택한 이유에 대해서도 설명합니다. 향후 게시글에서는 알고리즘 구현에 대한 자세한 내용을 설명할 것입니다.

개발 분산 합의

Mastodon 뉴스 리더 Gazzetta 출시

2024-12-21

Gazzetta는 Mastodon을 위해 특별히 설계된 뉴스 리더입니다. 소셜 네트워크 경험을 우선시하는 다른 Mastodon 클라이언트와 달리, Gazzetta는 플랫폼을 위한 RSS 리더와 같은 기능을 제공합니다. 별도의 인터페이스를 제공하여 뉴스와 링크 읽기에 집중할 수 있습니다. 트렌드 링크 확인, 전체 텍스트 검색, Safari 뷰어와의 통합, 북마크 관리, 링크 내보내기 등이 가능합니다. 글꼴 스타일, 썸네일 숨기기, 도메인, 키워드, 언어별 링크 필터링 등 광범위한 사용자 지정 옵션도 제공합니다.

개발 뉴스 리더

S2: 클라우드 시대 스트림 데이터 스토리지 혁신

2024-12-21

Bandar Systems는 클라우드 시대의 데이터 처리 방식에 혁신을 가져올 새로운 스트림 데이터 스토리지 서비스인 S2를 출시했습니다. 기존의 객체 기반 스토리지와 달리 S2는 스트림을 중심으로 하여 효율적이고 확장 가능하며 비용 효율적인 실시간 데이터 수집 및 처리 기능을 제공합니다. 고 처리량, 저지연 읽기 및 쓰기 작업을 지원하며 다양한 성능 및 비용 요구 사항을 충족하는 여러 스토리지 클래스를 제공합니다. S2는 Kafka 및 Kinesis와 같은 시스템을 대체하여 사용자에게 더욱 강력하고 유연한 스트림 데이터 관리 솔루션을 제공하는 것을 목표로 합니다.

(s2.dev)

기생형 SEO 운영자가 Google 패널티를 교묘하게 회피하다

2024-12-21

이 글에서는 기생형 SEO 운영자인 Finixio/Clickout Media가 Google 패널티를 얼마나 신속하고 효과적으로 회피했는지 밝힙니다. Google 알고리즘 업데이트 이후 Finixio/Clickout Media 계열의 여러 웹사이트가 Google의 사이트 평판 악용 정책 위반으로 심각한 패널티를 받아 트래픽과 순위가 급락했습니다. 하지만 교묘한 리다이렉트와 클로킹 기술을 사용하여 며칠 만에 사업을 재개하고 기생형 웹사이트 네트워크를 통해 이익을 계속 얻었습니다. 패널티 이후에도 사업을 확장하여 새로운 웹사이트와 기존의 높은 권위 사이트(CoinTelegraph 등)를 활용하여 도박과 암호화폐 홍보를 지속했습니다. 이 글에서는 콘텐츠의 위치 정보 기반 숨김 처리, 다양한 플랫폼에 대한 콘텐츠 배치 등 그들의 전략을 자세히 설명합니다. 그리고 이 현상의 근본 원인은 Google 알고리즘에서 토픽 권위가 약화되었고 도메인 권위가 주요 순위 요인이 되었다는 점을 지적합니다.

AP5 참조 매뉴얼: Common Lisp에 대한 논리 기반 확장

2024-12-21

AP5는 Common Lisp를 확장한 것으로, 사용자가 더욱 "명세 수준"에서 "프로그래밍"할 수 있도록 하며, 기계가 어떻게 하는지가 아니라 무엇을 해야 하는지에 중점을 둡니다. Lisp와 Gist 사양 언어의 측면을 결합하여 Gist의 컴파일 가능한 부분을 통합하고 성능 조정을 위한 주석 메커니즘을 제공합니다. AP5는 관계형 모델을 사용하여 데이터를 표현하고 데이터 액세스 및 조작을 위해 1차 술어 논리 언어를 지원합니다. 프로그래머는 관계, 규칙, 제약 조건을 정의하고 주석을 통해 성능을 최적화합니다. 이 매뉴얼에서는 AP5의 구문, 데이터베이스 작업, 규칙, 형식, 동등성, 구현 세부 정보를 자세히 설명하고 많은 예와 설명을 제공합니다.

FindMy.py: Apple FindMy 네트워크 쿼리용 올인원 Python 라이브러리

2024-12-21

FindMy.py는 Apple FindMy 네트워크를 쿼리하는 데 필요한 모든 것을 제공하는 Python 라이브러리입니다. 분산된 Find My 에코시스템을 통합하여 다양한 기기(AirTags, iDevices 등)와 인증 방법(SMS 및 Trusted Device 2FA 포함)을 지원하는 크로스 플랫폼 솔루션을 제공합니다. 비동기 및 동기 API를 모두 갖추고 있습니다. 현재 알파 단계이므로 API가 변경될 수 있지만 핵심 기능은 안정적입니다.

개발

MarkItDown: 무료 온라인 Markdown 변환기

2024-12-21

MarkItDown은 Word, PDF, HTML 등 다양한 파일 형식을 표준 Markdown으로 변환하는 무료 온라인 도구입니다. Microsoft의 오픈소스 프로젝트 Markitdown을 기반으로 하며, 블로그 작성, 노트 정리, 기술 문서 작성 및 콘텐츠 이전에 적합한 빠르고 안정적인 변환을 제공합니다. 다운로드나 설치 없이 파일을 업로드하기만 하면 깔끔하고 정돈된 Markdown 출력을 얻을 수 있습니다. 안전하고 효율적인 콘텐츠 관리를 위한 최선의 선택입니다.

기술 부채 vs. 기술 자산: 현명한 투자 전략

2024-12-21

이 글에서는 기술 부채와 기술 자산의 차이점을 설명합니다. 기술 부채는 재정적 부채와 마찬가지로 버그나 코드 가독성 저하 등 해결해야 하는 코드 문제이며, 개발 효율을 떨어뜨립니다. 반면 기술 자산은 고품질 SDK 구축 등 알려진 문제에 대한 선제적 투자이며, 미래 유지보수 비용을 절감하고 개발의 자유도를 높입니다. 이 글에서는 기술 자산에 투자하기 전에 기술 부채 상환을 우선시하고, 검증된 프로세스와 기술을 활용하여 기술 부채 축적을 방지함으로써 개발 효율과 제품 품질을 향상시키는 것을 권장합니다.

일반적인 확장 인라인 어셈블리 실수를 피하기 위한 규칙

2024-12-21

이 글은 인라인 어셈블리 튜토리얼이 아니라 일반적인 실수를 피하기 위한 6가지 규칙의 요약입니다. 저자는 인라인 어셈블리가 위험하며 가능하면 피해야 한다고 강조합니다. 최신 컴파일러는 대부분의 사용 사례에 대해 내장 함수를 제공합니다. 불가피한 경우 다음 규칙을 따르십시오. `volatile` 한정자를 사용하십시오. 메모리에 액세스하는 경우 `memory` 클로버를 사용하십시오. 입력 제약 조건을 변경하지 마십시오. 인라인 어셈블리에서 함수를 호출하지 마십시오. 절대 어셈블리 레이블을 정의하지 마십시오. 어셈블러의 로컬 레이블 기능을 사용하여 레이블 충돌을 방지하십시오. 이 글에서는 온라인 튜토리얼이나 LLM 생성 코드를 검토하여 이러한 규칙을 실천할 것을 권장합니다.

NoDB: 데이터베이스 없이 결제 처리하기

2024-12-21

알바로 두란의 "결제 엔지니어 플레이북"에서는 데이터베이스 없이 결제를 처리하는 혁신적인 결제 시스템 설계 개념을 소개합니다. 비동기 프로그래밍의 보편화는 데이터베이스의 필요성이라는 전제에 기반한다고 주장합니다. 이벤트 소싱을 사용하면 결제 프로세스의 각 단계는 영속적인 상태가 아니라 이벤트로 기록됩니다. 이러한 이벤트는 일시적으로 메모리에 저장되고 시스템은 이벤트 스트림에서 결제 상태를 재구성하므로 영속적인 스토리지는 필요하지 않습니다. 고주파 거래에서 영감을 얻은 이 고성능, 고신뢰성 접근 방식을 통해 핫 백업을 통한 신속한 장애 복구가 가능합니다. 이 기사에서는 결제 흐름의 예를 사용하여 이 개념을 자세히 설명하고 미래 결제 시스템에 대한 적용을 살펴봅니다.

@celine/bibhtml v3.0.3: Web Components 기반 참고문헌 시스템

2024-12-21

HTML 문서를 위한 Web Components 기반 참고문헌 시스템인 @celine/bibhtml이 3.0.3 버전을 출시했습니다. LaTeX/BibTeX 참조와 유사한 사용자 경험을 제공하는 것을 목표로 하며, Citation.js를 백엔드로 사용하고, 인용이나 참고문헌이 잘못된 형식이거나 JavaScript가 비활성화된 경우에도 적절히 동작합니다. BibTeX, 비정형 텍스트, DOI, Wikidata의 4가지 참고문헌 형식을 지원하며, ``, ``, ``의 3가지 사용자 정의 요소를 제공하여 HTML에서의 참고문헌 관리를 간소화합니다.

획기적인 발전: C 코드를 안전하게 Rust로 컴파일

2024-12-21

연구자들은 C 코드를 안전하게 Rust로 컴파일하는 새로운 방법을 개발했습니다. 이 기술은 정적 분석과 형식 지향 변환을 사용하여 Rust의 `unsafe` 블록에 대한 의존성을 피함으로써 메모리 안전성을 보장합니다. 이 방법은 HACL* 암호 라이브러리와 EverParse 라이브러리의 코드에 성공적으로 적용되어 8만 줄의 순수 Rust로 작성된 검증된 최신 암호 라이브러리가 생성되었습니다. 이는 업계 최초의 성과입니다.

개발 C 컴파일

Bash로 의사 3D 게임을 만든 프로그래머

2024-12-20

izabera라는 프로그래머가 Bash 스크립팅 언어를 사용하여 놀라울 정도로 정교한 의사 3D 게임을 개발했습니다. 이 프로젝트는 고전 게임 Wolfenstein 3D에 대한 오마주이며, GitHub에서 오픈 소스로 공개되어 있습니다. 코드는 간결하지만 결과는 훌륭하며, Bash의 기능과 프로그래머의 창의성을 보여줍니다. 저장소에는 게임 소스 코드와 데모 비디오가 포함되어 있습니다. 관심 있는 개발자는 GitHub에서 확인할 수 있습니다.

개발 게임 개발

모노레포 빌드 도구: 코드베이스 확장

2024-12-20

기존 빌드 도구는 대규모 코드베이스(100~10,000명의 활동 개발자)에서 어려움을 겪습니다. Bazel이나 Mill과 같은 모노레포 빌드 도구는 여러 언어, 사용자 정의 빌드 작업, 자동 캐싱 및 병렬 처리, 원격 캐싱 및 실행을 지원하여 빌드 속도와 효율성을 크게 향상시킵니다. 또한 종속성 기반 테스트 선택 및 빌드 작업 샌드박싱 기능을 통해 테스트 시간과 비결정성을 줄입니다. 이러한 기능은 소규모 프로젝트에서는 불필요해 보일 수 있지만, 대규모 프로젝트의 대규모 협업과 지속적 통합에는 필수적이며, 빌드 시간이 병목 현상이 되는 것을 방지합니다.

← Previous 1 3 4 5 6 7 8