블로그
전체 142025. 03. 23.
0
인프런 워밍업 클럽 스터디 3기 - Backend <3주 발자국>
package com.example.portfolio.presentation.controller import com.example.portfolio.presentation.dto.* import com.example.portfolio.presentation.service.PresentationService import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/api") class PresentationApiController( private val presentationService: PresentationService ) { // rest controller이기 때문에 httml response body에 리턴한다. @GetMapping("/test") //== @RequestMapping(method = [RequestMethod.GET], name = "/test") // 이 외에도 postMappring과 PutMapping이 존재한다. fun test(): String { return "OK" } @GetMapping("/v1/introductions") fun getIntroductions(): List { // 중요한 작업들은 서비스에서 다 해준다. return presentationService.getIntroductions() } @GetMapping("/v1/links") fun getLinks(): List { return presentationService.getLinks() } @GetMapping("/v1/resume") fun getResume(): ResumeDTO { return presentationService.getResume() } @GetMapping("/v1/projects") fun getProjects(): List { return presentationService.getProjects() } @GetMapping("/v1/articles") fun getArticles(): List { return presentationService.getArticles() } }package com.example.portfolio.presentation.repository import com.example.portfolio.domain.entity.* import com.example.portfolio.domain.repository.* import org.springframework.stereotype.Repository import java.security.PrivateKey @Repository class PresentationRepository( private val achievementRepository: AchievementRepository, private val introductionRepository: IntroductionRepository, private val linkRepository: LinkRepository, private val skillRepository: SkillRepository, private val projectRepository: ProjectRepository, private val experienceRepository: ExperienceRepository, private val articleRepository: ArticleRepository ) { fun getActiveAchivements(): List { return achievementRepository.findAllByIsActive(true) } fun getActiveExperiences(): List { return experienceRepository.findAllByIsActive(true) } fun getActiveIntroductions(): List { return introductionRepository.findAllByIsActive(true) } fun getActiveLinks(): List { return linkRepository.findAllByIsActive(true) } fun getActiveProjects(): List { return projectRepository.findAllByIsActive(true) } fun getActiveSkills(): List { return skillRepository.findAllByIsActive(true) } fun getArticles(): List { return articleRepository.findAll() } fun setArciels() { } } package com.example.portfolio.presentation.interceptor import org.springframework.context.annotation.Configuration import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @Configuration class PresentationInterceptorConfiguration( private val presentationInterceptor: PresentationInterceptor ) :WebMvcConfigurer { override fun addInterceptors(registry: InterceptorRegistry) { registry.addInterceptor(presentationInterceptor) .addPathPatterns("/**") .excludePathPatterns("/assets/**", "/css/**", "/js/**", "/admin/**", "h2**", "/favicon.ico", "/error") } } 느낀 점프리젠테이션 부분에서 화면과 인터셉터 설계를 하면서 아직 화면 구성과 데이터 조작에 어려움을 느껴서 진도를 많이 못나갔다. 지금부터 복습 다시하면서 천천히 자기서으로 만들어야 한다는 시간을 가져야 겠다고 생각했다.
2025. 03. 23.
0
인프런 워밍업 클럽 스터디 3기 - CS 3주차 운영체제 미션
메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.레지스터 - 가장 빠르고 CPU 명령 수행시 사용캐시 메모리 - 메모리와 레지스터 중간에서 데이터를 임시 저장함.메모리 - 프로그램 실행 시 데이터를 올려두는 공간보조 기억장치 - 비휘발성으로 데이터를 영구 저장 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요?경계 레지스터사용자 프로세스가 접근할 수 있는 메모리 범위를 지정하여 경계를 넘을 경우 차단하여 OS의 영역을 보호 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?가변 분할 방식 : 프로세스 크기에 맞춰 메모리를 할당하여 메모리 낭비가 적지만, 외부 단편화가 발생하여 조각 모음을 실행해야 함고정 분할 방식 : 조각 모음이 불필요하지만 내부 단편화 발생하여 메모리가 낭비되고 프로세스 크기가 제약된다. CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱 : 메모리가 부족하여 페이지 교체가 빈번히 일어나 스왑 작업에 더 많은 작업이 실행됨. HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요?반드시 필수는 아니지만 OS가 저장되거나 사용자 데이터를 비휘발성으로 저장되어야 하기 때문에 필요하다고 생각됨 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?파일시스템에서 파일로 해당 포인터만 제거하고 실제 데이터 블록은 남아있게 됨.
2025. 03. 23.
1
인프런 워밍업 클럽 스터디 3기 - OS <3주 발자국>
프로세스 동기화프로세스 간 통신(IPC: Inter-Process Communication)프로세스는 독립적인 실행 단위이기 때문에 기본적으로 타 프로세스의 메모리 영역에 접근할 수 없다. 서로 다른 프로세스들이 데이터를 교환하고 협력할 수 있도록 도와주는 메커니즘이다.통신의 분류는 크게 3가지로 분류될 수 있다.같은 컴퓨터 내 프로세스 간 통신같은 프로세스 내 스레드 간 통신네트워크를 이용한 원격 프로세스 간 통신같은 컴퓨터 내 프로세스 간 통신파일파일은 프로세스 들이 하나의 파일을 이용해 데이터를 쓰고 읽어서 데이터를 전달하는 방식이다.파이프파이프는 운영체제가 생성한 특별한 메모리 구조로 한 쪽 프로세스는 쓰고 다른 쪽 프로세스는 읽는 단방향 통신이다.익명 파이프(anonymous pipe) : 부모-자식 처럼 관련된 프로세스 간 통신명명 파이프(named pipe) : 이름이 있는 파일형태로 관련이 없는 프로세스 간 통신메시지 큐운영체제가 관리하는 큐 형태의 자료구조로 메시지 단위로 데이터를 주고 받는 형식이다. 비동기 통신이 가능하고 메시지 단위여서 데이터 구조화에 용이하다.공유 메모리여러 프로세스가 동시에 접근 가능한 메모리 영역을 운영체제가 할당하여 공유 메모리 영역을 통해 읽고 쓸 수 있다. 단 동기화 문제로 인해 세마포어, 뮤택스 등의 관리가 필요하다.시그널특정 이벤트(kill 혹은 ctrl+c)를 발생을 알리는 비동기 통신 메커니즘. 데이터 전달 보다는 프로세스 제어에 사용된다.스레드 간 통신스레드는 프로세스 내의 존재하는 실행 단위로 같은 프로세스내의 스레드들은 기본적으로 코드, 데이터, 힙 영역을 공유한다.이때 전역 변수, 힙 영역을 통해 데이터를 쉽게 주고 받는다.공유 자원에 대한 동시 접근 시 동기화가 매우 중요하여 동기화 도구들을 활용한다.뮤텍스(Mutex) : 한 번에 하나의 스레드만 공유 자원 접근할 수 있게 한다.조건 변수(Condition Variable) : 특정 조건이 충족될 때까지 스레드를 대기 상태로 두었다가 조건이 충족되면 통지하는 방식세마포어(Semaphore) : 0이상의 정수 값을 가지고 있고 0이되면 다른 스레드의 접근을 막는 방식. 프로세스가 자원을 얻을 때 wait(P 연산)으로 세마 포어 값을 감소시키고 signal(V 연산)으로 자원을 반환할 때 세마포어 값을 증가시킨다.wait(S): S -= 1 if (S 네트워크를 통한 원격 프로세스 간 통신소켓(Socket)네트워크 프로토콜(TCP/IP, UDP)를 활용하여 서로 다른 컴퓨터의 프로세스가 데이터를 주고 받을 수 있도록 하는 추상화된 인터페이스네트워크를 통해 통신 가능하며 표준화된 인터페이스라는 장점이 있지만, 데이터의 직렬화/역직렬화의 문제와 네트워크 프로그래밍의 복잡성이 존재한다.RPC(Remote Process Call)다른 컴퓨터에 있는 프로세스 함수를 로컬 함수 처럼 호출할 수 있도록 해주는 기술.내부적으로 소켓 통신 등을 사용하고 호출 과정, 파라미터 전달, 결과 반환을 추상화하여 원격 함수 호출을 구현할 수 있다.분산 시스템 개발에 용이하지만 성능 오버헤드 문제와 호출 실패 시 처리가 복잡하다.공유자원과 임계구역공유자원(Shared Resource)여러 프로세스(또는 스레드)들이 함께 사용하는 자원(메모리, 파일, 변수)를 의미한다.동시에 여러 프로세스가 공유하기 때문에 접근 순서에 따라 결과가 달라질 수 있다.컨텍스트 스위칭등으로 인해 결과를 예측하기 어려운 상황이 발생하며 이를 동기화 문제 (Synchronization Problem)이라고 한다.임계구역(Critical Section)공유 자원에 접근할 때 반드시 보호해야 하는 코드 영역여러 프로세스가 동시에 임계구역에 접근하면 의도치 않은 결과가 발생하게 된다.접근 순서와 타이밍 문제로 발생하는 경쟁적 상태를 경쟁 조건(Race Condition)이라고 한다.임계 구역 문제 해결을 위한 조건상호 배제(Mutual Exclusion)한 번에 단 하나의 프로세스만 임계구역에 접근 가능진행(Progress)임계구역을 사용하지 않는 프로세스가 다른 프로세스의 접근을 방해하지 않아야 한다.유한 대기(Bounded Waiting)어떤 프로세스든 일정한 시간 내에 반드시 임계 구역에 진입할 수 있어야 한다.프로세스의 기아 상태(Starvation)를 피하고 프로세스의 공평한 자원 할당이 필요하다.임계구역 문제 해결 방법세마포어운영체제 차원에서 제공하는 동기화 도구로 공유자원의 접근 권한을 관리하는 정수형 변수이다.세마포어의 값은 일반적으로 자원의 개수를 의미하며 보통 이진(0,1) 세마포어를 사용하여 임계 구역을 보호한다.공유 자원에 접근하려는 프로세스를 대기 큐에 넣고 운영체제가 대기 큐에 있는 프로세스에게 세마포어를 통해 접근 권한을 제어한다.두 가지 기본 연산을 통해 관리한다.wait() : 자원이 없으면 프로세스는 자원이 확보될 때까지 대기 상태로 들어간다.signal( ) : 자원을 사용한 후 다시 자원을 반납하여 대기중인 프로세스에게 공유자원을 사용하게 한다.int s = 1; wait(s) // 임계 구역 사용 signal(s) 단 signal()과 wait()의 호출 순서가 잘못되면 세마포어 값이 혼동되어 데이터 손상이나 교착상태(deadlock)를 유발할 수 있다.뮤텍스(Mutex)세마포어의 특수한 형태로 오직 한 프로세스만 임계구역 접근을 허용하는 이진 세마포어(binary semaphore)이다.C언어에서 POSIX 스레드 API를 사용하면 pthread_mutex_lock()과 pthread_mutex_unlock()으로 표현된다.모니터운영체제가 아니라 프로그래밍 언어 수준에서 제공하는 동기화 메커니즘이다.모니터는 공유 데이터와 이를 조작하는 프로시저(메서드)를 하나의 객체로 묶은 형태로 제공한다.프로그래머는 직접 wait(), signal()을 호출할 필요 없이 언어에서 자동으로 동기화 관리를 한다.public class SharedCounter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }자바에서는 synchronized 키워드를 통해 동기화 코드를 작성하게 된다.synchronized 메서드를 호출하는 동안에는 다른 synchronized 메서드가 접근 불가능 핟록 배타적 접근이 보장된다.데드락(Deadlock)데드락여러 프로세스가 한 프로세스의 작업을 끝나기 까지 기다리다가 작업을 진행하지 못하는 상태를 교착 상태(데드락)이라고 한다.교착상태는 공유 자원 때문에 발생한다. 공유 자원이 없다면 교착상태는 발생하지 않는다.식사하는 철학자 포크가 3개인 식사에서 2개를 사용하여 식사를 하는 데 동시에 포크를 집어 더 이상 포크가 없는 상태이다.필요조건데드락이 발생하려면 다음과 같은 조건이 존재한다.공유자원에는 상호 배제가 일어난다. 공유 자원은 다른 프로세스가 사용할 수 없는 리소스이다.비선점이다. 한 프로세스가 선점하고 있을 때 다른 프로세스는 선점할 수 없다.점유와 대기. 어떤 프로세스가 리소스 A를 원하는 상태에서 리소스 B를 원하는 상태여야 한다.원형 대기. 점유와 대기를 하는 프로세스들의 관계가 원형을 이루고 있다는 것이다.데드락의 예방은 매우 비효율적이고 해결하는 쪽으로 이 문제를 해결해야 겠다고 생각했다.데드락 해결교착상태 회피전체 자원과 할당된 자원을 기준으로 안정상태와 불안정상태를 점검하고 유지한다.은행원 알고리즘은행은 대출을 원하는 사람들에게 대출을 해준다. 은행에 돈이 없고 대출자들에게 돈을 받지 못한다면 은행은 돈을 빌려줄 수 없다.운영체제는 프로세스에게 자원을 할당하기 전에 전체 자원을 알고 있어야 한다. 이를 시스템의 총 자원이라고 한다. 프로세스는 각 자원이 필요한 최대 숫자를 운영체제 에게 알려준다.안정상태는 프로세스마다 최대 요구자원과 현재 할당된 자원, 요청이 예상되는 자원을 계산한다. 최대 요구자원 - 현재 할당된 자원으로 요청이 예상되는 자원을 계산한다. 프로세스가 운영체제의 자원을 요구하면 프로세스의 요청이 예상되는 자원을 충족하는 곳에 자원을 할당하고 프로세스의 작업이 끝나 반납하는 경우 다른 프로세스에 자원을 할당한다.불안정 상태는 현재 프로세스에 할당이 되어 있는 데 현재 시스템의 사용 가능 자원이 다른 프로세스의 최대 요구 자원을 충족시키지 못하는 상태를 말한다.교착상태 검출가벼운 교착상태 검출프로세스가 일정 상태동안 작업을 수행하지 않는다면 교착상태가 일어난 것으로 간주하고 교착상태를 해결한다.일정 시점마다 체크포인트로 작업을 저장하고 교착상태를 해결하기 위해 일정 시점으로 프로세스를 되돌린다.무거운 교착상태 검출자원할당 그래프를 통해 운영체제가 현재 어떤 프로세스가 자원을 사용하는 지 확인하고 교착상태에서 해결한다.프로세스는 각 자원을 차지하고 있고 다른 자원을 요청하고 있다. 순환 구조가 생긴다면 교착상태를 일으킨 프로세스를 강제 종료한다. 그리고 다시 실행시킬때 체크포인트로 롤백 시킨다.OS가 지속적으로 자원할당 그래프를 통해 그래프를 지켜봐야 하고 유지해야 하기 때문에 오버헤드가 발생한다.가벼운 교착상태 검출에서 발생하는 억울하게 종료되는 프로세스는 발견되지 않는다.쉬어가기컴파일과 프로세스코드가 어떻게 프로세스로되고 메모리로 할당되는가?컴파일 언어는 컴파일을 거쳐 기계어로 실행파일을 만든다. 기계어로 실행파일을 만들기 때문에 속도가 빠르다.인터프리터는 실행시 코드를 한 줄식 해석하여 만들어 낸다.프로세스의 메모리 구조는 코드영역, 데이터영역, 스택과 힙 영역으로 이루어진다.컴파일된 프로그램은 어떻게 프로세스를 차지하게 되는가?코드는 -> 전처리기 -> 컴파일러 -> 어셈블러 -> 링커 -> 실행파일로 각 단계를 거쳐 바뀌게 된다.프로세스는 이런 파일을 실행할 때 pcb를 만들고 cpu 스케쥴러에 의해 실행되고 멈춘다.메모리메모리 종류여러 종류의 메모리가 존재한다. 왜 컴퓨터에 여러 메모리 종류가 필요할까?레지스터 : 가장 빠른 기억장소로 cpu 내에 존재한다. cpu의 32비트와 64비트는 레지스터의 크기를 의미한다. cpu는 계산할 때 메모리에서 값을 불러와 레지스터에 저장한 뒤 값뒤 메모리에 저장한다.캐시 : 메모리에 있는 값을 레지스터로 가져오기 위해서는 캐시에 미리 저장해 둔다.메모리 :보조저장장치 : 전원이 공급되지 않아도 저장이 되는 보조저장창치이다.메모리와 주소폰 노이만 구조는 모든 프로그램을 메모리에 올려 실행한다. 멀티 프로그래밍 환경에서는 여러 프로그램이 메모리에 올라간다.메모리는 1바이트마다 주소를 남긴다.cpu bit에 따라 64비트 레지스터는 한번에 여러 처리를 할 수 있어 프로그램 처리 능력이 향상된다.물리 주소를 몰라도 논리주소를 통해 프로그램에 접근할 수 있다. 운영체제는 특별하기 때문에 운영체제를 따로 만든다. 하드웨어 적으로 경계 레지스터를 만들어 프로세스가 경계 레지스터를 침범하지 못하게 만든다.절대주소(물리주소공간)와 상대주소(논리주소공간)라는 개념이 존재한다. 메모리 관리자(MMU)는 사용자가 접근할 때마다 접근 주소를 재배치 레지스터에 따라 데이터에 접근하여 값을 가져온다.메모리 할당방식메모리에 비해 프로그램의 크기가 클 경우 실행 시켜야할 부분만 메모리에 올리고 나머지는 디스크에 저장한다. 이를 오버레이라고 한다.메모리에 여러 프로세스가 올라오는 환경에서는 두 가지 방식으로 나뉜다.가변 분할 방식 : 프로세스 크기에 따라 메모리를 할당한다. 세그멘테이션이라고도 부른다. 프로세스가 종료되면 메모리가 내려가지만 외부 단편화가 발생할 수 있다. 조각 모음을 하려면 프로세스의 작업을 일시 중단해야 한다. 따라서 오버헤드가 발생한다.고정 분할 방식 : 프로세스 크기와 상관없이 고정으로 메모리를 할당한다. 페이징이라고도 부른다. 내부단편화를 통해 메모리에 낭비가 발생한다.버디 시스템 : 위의 둘을 합친 방식이다. 전체 메모리를 2로 나누어 프로세스 단까지 나누고 프로세스를 할당한다. 이 방식의 장점은 세그멘테이션 처럼 프로세스를 잘 할당하고 내부 단편화가 크게 나타나지 않는다.가상메모리가상 메모리 개요컴퓨터 마다 메모리의 크기는 다르다. 그래서 가상메모리를 사용한다.하드 디스크 내의 스왑 영역으로 옮기고 거기에서 메모리에서 꺼내와서 실행한다.MMU는 부족한 물리 메모리와 메모리 변환 과정같은 복잡한 과정을 거친다.MMU는 매핑 테이블을 통해 프로세스와 위치를 저장한 뒤 사용한다.세그멘테이션프로그램 관점에서 메모리는 서로 인접할 필요가 없지만 프로세스는 각 비슷한 위치에 존재한다.사용자 프로세스, cpu는 논리주소를 사용한다. MMU가 고정 주소로 변환해준다.MMU는 세그먼트 번호와 base address와 bound address를 가지고 있고 이를 통해 메모리를 관리한다.세그멘테이션은 메모리를 분할할수 있고 각 영역을 모듈로 처리하여 분할이 쉽다.페이징논리주소공간과 물리주소공간이 있다. 페이징에서 논리주소공간은 일정한 크기로 나뉜다. 물리주소공간 역시 일정하게 나뉜다.MMU는 페이지 테이블을 가지고 있다. 인덱스와 프레임으로 이루어져 있다. 논리주소를 페이지의 크기로 나누어 페이지 넘버를 구한다. 오프셋은 논리주소 % 페이지의 크기이다. 페이지 넘버와 오프셋을 통해 프레임 번호에 오프셋을 더해 물리주소를 알아낸다.페이징은 모두 크기가 같아 바운드 어드레스가 필요없다.페이지드 세그멘테이션세그멘테이션과 페이징을 혼합하여 단점을 취한다.가변분할 방식으로 각 영역을 공유하기 편하고 메모리 접근 보호에 편하다. 페이징은 메모리를 효율적으로 관리할 수 있다.메모리 접근 권한은 읽기 쓰기 실행으로 나뉜다. 각 영역마다 접근 권한이 있다. 코드는 읽기, 쓰기, 데이터는 읽기, (쓰기), 스택과 힙은 읽기, 쓰기 권한이 있다.각 권한은 MMU가 관리하는데 권한이 없다면 프로세스를 종료한다.세그먼트 번호, 권한비트, 페이지넘버, 페이지 수로 MMU는 테이블을 관리한다.디맨드 페이징(가져오기 정책)프로세스가 실행될때 모든 모듈이 메모리에 올라오는 것이 아니라 필요한 부분만 올라온다.지역성 이론 : 공간의 지역성으로 현재 위치와 가까운 데이터에 접근한다. 시간의 지역성으로 최근 접근했던 데이터가 오래 전에 접근했던 데이터보다 접근할 가능성이 높다.디맨드 페이징은 사용될거 같은 데이터는 메모리에 올리고 다른 데이터는 스왑영역으로 보낸다.페이지 테이블 엔트리는 다음 구조이다. 접근비트, 변경비트(변경 유무), 유효비트, 읽기,쓰기,실행비트(권한비트), 프레임물리 메모리에 없다면 스왑영역에 접근한 뒤 메모리에 올라가게 되고 프로세스는 다시 실행된다.페이지 교체 정책스왑영역에서 메모리를 불러들이는데 메모리에 공간이 없다면 메모리에 어떤 페이지를 스왑영역으로 보낼지 결정하는 것이 페이지 교체 정책이다.랜덤으로 다양하게 보내기가장 오래된 페이지를 보내기가장 오랫동안 쓰이지 않을 페이지를 보내기최근 가장 사용이 적은 페이지를 보낸다.(LRU)LRU가 가장 좋은 방법인데 시간은 오버플로우가 나므로 일정시간마다 초기화하는 접근을 초기화 하는 클락 알고리즘을 사용한다.클락 알고리즘은 스왑이 일어나야 할때 접근 되지 않은 페이지를 스왑으로 보낸다.접근 비트와 변경 비트를 사용하여 둘다 0인 페이지 를 스왑으로 보내는 향상된 클락알고리즘도 있다.스레싱과 워킹셋멀티프로그래밍이 늘어나면 물리 메모리에 올려야할 데이터가 늘어나고 스왑해야할 작업이 늘어난다.cpu 사용률이 더 낮아진다. 이를 스레싱이라고 부른다.스레싱은 물리 메모리의 용량이 너무 낮은 것에 기인한다. 충분한 크기라서 스레싱이 발생하지 않는다면 성능향상은 일어나기 힘들다.프로세스가 실행되면 page fault가 발생할 때 페이지를 더 많이 할당한다. page fault가 일어나지 않는다면 페이지 수를 줄여 적절한 페이지를 유지한다.워킹셋은 프로세스가 실행상태가 되는 컨텍스트 스위칭이 일어날 때 사용한다. 워킹셋을 한꺼번에 올리고 내린다.입출력 장치주변장치주변장치는 메인보드에 있는 버스로 연결된다. 주소버스 데이터버스, 컨트롤버스로 연결된다.장치의 상태를 보관하는 레지스터로 입출력 작업에서 저장된다.캐릭터 디바이스와 블록 디바이스로 구분된다.전송단위가 캐릭터인지 블록인지로 구분된다.캐릭터 디바이스는 상대적으로 적은 데이터를 전송하고 블록 디바이스는 큰 단위로 연결한다.마우스/키보드마우스와 키보드는 캐릭터디바이스 이다.디바이스 컨트롤러가 어떤 동작을 했는지 받고 운영체제에 이벤트를 넘기고 운영체제는 프로그램으로 데이터를 넘겨 동작을 수행한다.하드디스크 / Flash Memory(SSD)하드디스크는 플래터와 스핀들이 존재한다. 헤드와 디스크암으로 자성으로 하드 디스크의 섹터 단위로 데이터를 읽는다.ms 단위로 움직이는데 다른 장치들은 ns단위로 읽으므로 상대적으로 느리게 인식된다.flash memory는 기계적으로 헤드를 움직이는 것이 아니라 전기적으로 데이터를 읽는다. 그로인해 하드디스크보다 상대적으로 더 우위에 있다.파일시스템파일과 파일시스템파일은 하드디스크와 ssd에 저장된다. 사용자는 운영체제를 통해 안전하게 저장한다.운영체제는 파일시스템을 통해 파일을 관리한다. 파일관리자는 파일테이블을 통해 파일을 관리한다.파일과 디렉토리를 만든다.파일과 디렉토리 수정/삭제한다.파일의 권한을 관리한다.무결성을 보장한다.백업과 복구를 한다.파일을 암호화한다.디스크는 전송단위가 블록이다. 파일관리자가 중간에서 관리한다.windows의 경우 확장자를 통해 파일의 구분을 하고 바로 연결되는 프로그램을 관리할 수 있다.파일은 헤더와 데이터로 구분한다. 헤더에는 확장자나 사용자 등이 저장된다.파일 디스크립터는 저장장치에 존재하다가 파일을 호출하면 파일을 호출할 수 있다.순차파일구조는 파일이 순차적으로 저장되어 있다. 모든 데이터가 순서대로 기록되기 때문에 공간의 낭비가 없고 단순하다. 특정 지점으로 이동이 어려워 삭제와 수정이 어렵다.직접파일구조는 해시 함수를 통해 저장위치를 파악하여 저장한다. json도 이러한 방식이다. 주소에 따라 데이터를 저장한다. 해시 테이블을 이용하기 때문에 저장과 접근이 빠르지만 해시 함수를 잘 선정해야 한다.인덱스파일구조는 두 가지 방식이 모두 가능하다. 파일은 순차로 저장되어 있는데 인덱스 테이블을 통해 블록 번호를 알아내어 바로 데이터에 접근한다.디렉토리디렉토리는 1개 이상파일과 디렉토리를 가질 수 있다. 루트는 /를 사용하고 구분을 통해 사용한다. 디렉토리 역시 파일이다. 단지 디렉토리에는 파일정보가 저장되어 있다.파일과 디스크파일제어 테이블은 파일안에 여러개의 블록으로 나누어 저장한다.불연속할당(연결할당) 연결리스트를 통해 해당 노드가 포인터를 통해 다른 노드를 이동하여 데이터를 조회한다. 시작 노드만 가지고 있고 이를 순회하다가 null을 만날때까지 저장하여 모든 데이터를 가져온다.불연속할당(인덱스할당) 데이터의 인덱스를 가지고 있는 노드를 통해 가져온다.빈공간을 계속 찾는 것은 비효율적이다. 효율적 관리를 위해 free block list를 통해 비어있는 블록을 남겨놓는다. 사용자는 데이터를 지워도 파일할당 테이블의 데이터를 남기므로 포렌식을 통해 복구할 수 있다.
2025. 03. 23.
0
인프런 워밍업 클럽 스터디 3기 - CS 3주차 알고리즘 미션
지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요.버블정렬, 선택정렬 , 삽입정렬시간복잡도 O(n^2)장점 : 이해가 쉽고 구현이 간단하다.단점 : 다른 두 알고리즘에 비해 시간복잡도가 높다. 병합정렬, 퀵 정렬시간복잡도 : O(nlogn)장점 : 시간복잡도가 오래 걸리지 않는 효율적인 알고리즘. 퀵 정렬의 경우 메모리 사용량이 병합정렬에 비해 더욱 뛰어나다.단점 : 이해가 어렵고 구현이 어렵다. 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요. 타뷸레이션의 상향식 계산이 메모이제이션에 비해 재귀 함수에 의한 콜스택 메모리를 사용하지 않으므로 타뷸레이션의 상향식 계산이 더 유리하다.
2025. 03. 23.
0
인프런 워밍업 클럽 스터디 3기 - DS/AL <3주 발자국>
알고리즘재귀(Recursion)어떠한 것을 정의할 때 자기 자신을 참조하는 것. 복잡한 문제를 간단한 하위 문제로 나누어 해결할 수 있다.재귀적으로 정의한 함수를 재귀함수라고 한다.function func(number) { console.log(number) return number+1; } func(number)콜스택의 메모리 공간만큼 찬 뒤에 종료된다.기저 조건이 없기 때문에 언제 종료될 지 모른다.재귀 함수는 기저 조건이 반드시 있어야 한다.function func(number) { if (number >= 11) { return ; } console.log(number) return number+1 }콜스택은 함수가 호출되며 올라가는 메모리 영역이다.콜스택은 filo 구조이다. 함수가 호출 될 때마다 콜스택에 쌓이고 함수가 종료되면 콜 스택에서 제거된다.콜스택의 메모리가 가득 차면 더 이상 쌓일 스택이 없어서 제거된다.재귀적으로 생각하기패턴 1단순히 반복 실행. 이 방법은 콜스택의 공간을 많이 차지 하여 단순 반복문에 비해 좋지 않다.패턴 2하위 문제의 결과를 기반으로 현재 문제를 계산(하향식 계산)하향식 계산은 주로 재귀 함수를 통해 구현가능하다.// 주어진 배열의 합을 구하는 문제 // 부분 배열에 마지막 원소를 더해 답을 도출할 것임 function sumArray(arr) { if (arr.length == 0) { return null; } if (arr.length == 1) { return arr[0]; } return sumArray(arrs.slice(0, -1)) + arr[arr.length - 1] }// 문자열의 길이를 계산하는 문제 function strLength(arr) { if (arr.length == 0) { return 0; } return strLength(arr.slice(0, -1)) + 1; }function power(x, n) { if (n 하노이의 탑재귀 함수의 하향식 계산을 보여줄 수 있는 방법function hanoi(count, from, to, temp) { if (count == 0) { return ; } hanoi(count-1, from, temp, to) console.log(`원판 ${count}를 ${from}에서 ${to}로 이동`) hanoi(count-1, temp, to, from) } hanoi(3, "A", "C", "B")정렬 - 버블정렬정렬 알고리즘은 여러 가지가 존재한다.버블정렬은 현재 숫자와 비교하는 숫자의 비교하여 위치를 바꾸는 방법이다.현재 숫자가 크다면 비교하는 숫자와 위치를 바꾸어서 제일 마지막에 가장 큰 값을 위치시킨다.이런 식으로 제일 마지막에 있는 숫자는 정렬에 초기화 하지 않고 하나 씩 줄여서 정렬을 계산시킨다.function BubbleSort(arr) { for (let i =0; i arr[j + 1]) { let temp = arr[j] arr[j] = arr[j+1] arr[j+1] = temp } } } }n을 거칠때마다 마지막의 원소가 하나 씩 정렬된다.O(n^2 / 2)이 되는데 자연수는 제거한다. 따라서 O(n^2)이 된다.장점 : 가장 쉽게 생각할 수 있고 이해와 구현이 간단함.단점 : O(N^2)이므로 성능이 좋지 못하다.정렬-선택정렬선택정렬은 정렬 되지 않은 원소와 가장 작은값을 첫 번 째 원소로 가져온 뒤 그 부분을 치환하고 계속해서 정렬해 나간다.function SelectSort(arr) { for (let i= 0; i 성능은 버블 정렬과 같이 O(n^2)이 된다.장점 : 이해와 구현이 쉽다.단점 : 성능이 좋지 못하다.정렬 - 삽입정렬삽입정렬은 정렬되지 않은 영역의 가장 앞에 있는 숫자를 하나씩 정렬된 영역에 삽입하며 진행한다.function InsertionSort(arr) { for (let i = 1; i = 0; j--) { if (arr[j] > insertingData) { arr[j + 1] = arr[j] } else { break; } } arr[j + 1] = inseringData } }성능은 선택정렬과 같이 O(n^2)이 된다.장점 : 이해와 구현이 쉽다.단점 : 성능이 좋지 못하다.정렬 - 병합정렬분할정복을 이용한 정렬이다.배열을 반으로 줄이고 이걸 계속 반복하여 1이 나올때까지 분할한 뒤 두 배열을 정렬하여 하나의 배열로 병합하는 것이다.function MergeSort(arr, leftIndex, rightIndex) { if (leftIndex midIndex) { for (let i = rightAreaIndex; i 장점 : O(nlogn)으로 성능이 좋음단점 : 이해와 구현이 어려움정렬 - 퀵정렬pivot을 설정하여 피봇을 기준으로 정렬하는 정렬방법이다.function quickSort(arr, left, right) { if (left = arr[leftStartIndex]) { leftStartIndex+= 1 } while (rightStartIndex >= left + 1 && pivot 퀵 정렬이 더 적은 비교와 더 나은 메모리를 사용하기 때문에 퀵 정렬이 더 좋은 알고리즘으로 평가받는다.동적 프로그래밍 - 메모이제이션분할 정복을 통해 문제를 해결할 경우 저장 장치를 사용하여 시간 복잡도를 많이 줄일 수 있다.// 메모이제이션이 없을 경우 function fibo(n) { if (n == 0 || n == 1) { return n } return fibo(n-1) + fibo(n-2) }위의 코드는 중복으로 계산되는 식이 매우 많다.메모이제이션은 계산결과를 저장하여 사용하는 것이다.해시테이블을 사용하여 메모이제이션을 구현해보자.function fibo(n, memo) { if (n == 0 || n == 1) return n if (memo[n] == null) { memo[n] = fibo(n-2,memo) + fibo(n-1, memo) } return memo[n] }메모이제이션을 사용하지 않는다면 O(n^2)이고 메모이제이션을 사용하면 O(n)이 소모된다.동적 프로그래밍 - 타뷸레이션타뷸레이션은 상향식 계산방식으로 계산에 필요한 모든 값을 계산 후 테이블에 저장해둔다.function fibo(n) { if (n == 0 || n == 1) {return n} let table = [0,1] for (let i = 2; i 메모이제이션은 공간을 차지하고 콜 스택으로 인한 함수 호출이 걸리지만 타뷸레이션은 콜 스택 메모리를 사용하지 않는다.메모이제이션은 재귀적으로 문제를 하위문제로 나눈다. 어떤 문제의 경우에는 재귀가 직관적이지 않다. 따라서 타뷸레이션을 사용하는 것이 더 좋을 수도 있다.
2025. 03. 16.
0
인프런 워밍업 클럽 스터디 3기 - backend <2주 발자국>
package com.example.portfolio.domain.repository import com.example.portfolio.domain.constant.SkillType import com.example.portfolio.domain.entity.* import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions.* import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest @DataJpaTest // jpa 관련 컴포넌트만 로드하여 테스트 수행 @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ProjectRepositoryTest( @Autowired val projectRepository: ProjectRepository, @Autowired val skillRepository: SkillRepository ) { val DATA_SIZE = 10 private fun createProject(n: Int): Project { val project = Project( name = "${n}", description = "테스트 설명 {n}", startYear = 2023, startMonth = 9, endYear = 2023, endMonth = 9, isActive = true ) val details = mutableListOf() for (i in 1..n) { val projectDetail = ProjectDetail(content = "테스트 ${i}", url = null, isActive = true) details.add(projectDetail) } project.addDetails(details) val skills = skillRepository.findAll() // List 반환 val skillsUsedInProject = skills.subList(0, n) // 0부터 n까지의 리스트 반환 for (skill in skillsUsedInProject) { val projectSkill = ProjectSkill(project = project, skill = skill) project.skills.add(projectSkill) } return project } @BeforeAll // 인스턴스 메소드 사용 fun beforeAll() { println("----- 스킬 데이터 초기화 시작 -----") val skills = mutableListOf() for (i in 1..DATA_SIZE) { val skillTypes = SkillType.values() val skill = Skill(name = "테스트 ${i}", type = skillTypes[i % skillTypes.size].name, isActive = true) skills.add(skill) } skillRepository.saveAll(skills) println("----- 스킬 데이터 초기화 종료 -----") println("----- 데이터 초기화 이전 조회 시작 -----") val beforeInsert = projectRepository.findAll() assertThat(beforeInsert).hasSize(0) println("----- 데이터 초기화 이전 조회 종료 -----") println("----- 테스트 데이터 초기화 시작 -----") val projects = mutableListOf() for (i in 1..DATA_SIZE) { val project = createProject(i) projects.add(project) } projectRepository.saveAll(projects) println("----- 테스트 데이터 초기화 종료 -----") } @Test fun testFindAll() { println("----- findAll 테스트 시작 -----") val projects = projectRepository.findAll() assertThat(projects).hasSize(DATA_SIZE) println("projects.size: ${projects.size}") for (project in projects) { assertThat(project.details).hasSize(project.name.toInt()) println("project.details.size: ${project.details.size}") assertThat(project.skills).hasSize(project.name.toInt()) println("project.skills.size: ${project.skills.size}") } println("----- findAll 테스트 종료 -----") } @Test fun testFindAllByIsActive() { println("----- findAllByIsActive 테스트 시작 -----") val projects = projectRepository.findAllByisActive(true) assertThat(projects).hasSize(DATA_SIZE) println("projects.size: ${projects.size}") for (project in projects) { assertThat(project.details).hasSize(project.name.toInt()) println("project.details.size: ${project.details.size}") assertThat(project.skills).hasSize(project.name.toInt()) println("project.skills.size: ${project.skills.size}") } println("----- findAllByIsActive 테스트 종료 -----") } }1. @DataJpaTestJPA 관련 테스트를 실행하도록 하는 어노테이션JPA 관련 컴포넌트만 로드한다.임베디드 데이터베이스(H2, HSQL 등)를 사용하고 자동으로 롤백된다.테스트의 독립성을 보장한다.2. @BeforeAll초기 데이터를 세팅하는 데 붙이는 메서드에 붙이는 JUnit 어노테이션한 번만 실행되며 데이터베이스 어노테이션@TestInstance(TestInstance.Lifecycle.PER_CLASS) 와 함께 사용해야 함 Lazy vs EagerLazy : 연관된 엔터티에 실제로 접근할 때 조회된다.Eager : 엔터티를 조회할 때 연관된 엔터티도 함께 조회한다.#### 소감이번 주는 많이 못했어도 다음 주에는 많이 할 예정입니다.