동기(Synchronous)와 비동기(Asynchronous) 프로그래밍의 차이점은 무엇인가요?

김상욱·2024년 11월 14일

동기(Synchronous)와 비동기(Asynchronous) 프로그래밍의 차이점은 무엇인가요?

동기(Synchronous Programming)

  • 작업을 순차적으로 수행하며, 하나의 작업이 완료된 후에 다음 작업을 진행합니다. 작업이 끝날 때까지 호출한 주체는 작업 완료를 대기해야 합니다. 결과를 바로 확인할 수 있어 흐름이 단순하고 직관적입니다. 작업 시간이 길어지면 응답성이 낮아질 수 있습니다.
  • 코드가 읽기 쉽고 디버깅이 용이합니다. 실행 순서가 예측 가능하여 로직이 단순합니다.
  • 하나의 작업이 오래 걸리면, 다음 작업이 지연됩니다. CPU와 I/O 리소스를 비효율적으로 사용할 수 있습니다.
console.log("작업 시작");
alert("사용자 입력 대기 중...");
console.log("작업 완료"); // alert 창을 닫기 전까지 실행되지 않음

비동기 프로그래밍(Asynchronous Programming)

  • 작업을 병렬적으로 처리하며, 작업 완료 여부와 상관없이 다음 작업을 진행합니다. 작업이 완료되면 콜백, Promise, 또는 이벤트를 통해 결과를 반환받습니다.
  • 작업 수행 중에도 다른 작업을 계속 진행할 수 있습니다. 작업이 완료되었을 때 별도로 알림을 받습니다. 주로 네트워크 통신, 파일 I/O, 데이터베이스 조회 등 시간이 오래 걸리는 작업에서 사용됩니다.
  • 리소스 효율성이 높아지고, 응답성이 좋아집니다. 여러 작업을 동시에 처리할 수 있어 사용자 경험(UX)이 향상됩니다.
  • 코드의 복잡도가 증가할 수 있습니다. (콜백 헬 문제) 디버깅과 에러 처리가 어려울 수 있습니다.
console.log("작업 시작");
setTimeout(() => {
  console.log("비동기 작업 완료");
}, 2000); // 2초 후 실행
console.log("작업 완료"); // 비동기 작업과 상관없이 바로 실행

: 콜백 헬(Callback Hell)
콜백 헬은 비동기 프로그래밍에서 발생하는 문제로, 비동기 작업을 처리하기 위해 중첩된 콜백 함수들이 다단계로 연결되면서 코드가 복잡하고 가독성이 떨어지는 상황을 의미합니다. 코드가 계단처럼 깊게 중첩되기 때문에 지옥이라는 표현을 사용.
-> 비동기 작업을 순차적으로 처리하거나, 결과를 활용하여 다음 작업을 실행해야 하는 경우 콜백을 중첩시켜야함. 많은 비동기 작업이 서로 의존성을 가질 경우.

getData1((result1) => {
  console.log("데이터 1 처리 완료");
  getData2(result1, (result2) => {
    console.log("데이터 2 처리 완료");
    getData3(result2, (result3) => {
      console.log("데이터 3 처리 완료");
      getData4(result3, (result4) => {
        console.log("모든 데이터 처리 완료:", result4);
      });
    });
  });
});

해결 방법
1. Promise 사용 : then 체인을 통해 중첩을 줄일 수 있습니다.

getData1()
  .then((result1) => {
    console.log("데이터 1 처리 완료");
    return getData2(result1);
  })
  .then((result2) => {
    console.log("데이터 2 처리 완료");
    return getData3(result2);
  })
  .then((result3) => {
    console.log("데이터 3 처리 완료");
    return getData4(result3);
  })
  .then((result4) => {
    console.log("모든 데이터 처리 완료:", result4);
  })
  .catch((error) => {
    console.error("에러 발생:", error);
  });
  1. Async/Await 사용 -> 동기 코드처럼 읽기 쉬운 비동기 코드를 작성할 수 있음
async function processData() {
  try {
    const result1 = await getData1();
    console.log("데이터 1 처리 완료");
    const result2 = await getData2(result1);
    console.log("데이터 2 처리 완료");
    const result3 = await getData3(result2);
    console.log("데이터 3 처리 완료");
    const result4 = await getData4(result3);
    console.log("모든 데이터 처리 완료:", result4);
  } catch (error) {
    console.error("에러 발생:", error);
  }
}

processData();

Spring 기반의 자바 개발자라면 Node.js 대신 Spring Framework와 자바 기반의 기술 스택으로 실습을 진행하면 됩니다. 동기/비동기 프로그래밍, 콜백 헬 해결, 그리고 비동기 처리 방법은 자바에서도 동일하게 적용할 수 있습니다. 아래는 Spring Framework를 활용한 실습 아이디어입니다.


1. 동기와 비동기 프로그래밍 실습

실습 목표

  • 자바에서 동기와 비동기의 차이를 이해하고, Spring에서 비동기 작업을 구현해보기.

실습 아이디어

  1. 동기 처리 예제

    • REST API를 동기적으로 처리하는 기본 컨트롤러 작성.
    • 작업이 완료될 때까지 클라이언트가 대기하도록 구현.
    @RestController
    public class SyncController {
        @GetMapping("/sync")
        public String syncExample() throws InterruptedException {
            Thread.sleep(3000); // 3초 대기
            return "동기 작업 완료";
        }
    }
  2. 비동기 처리 예제

    • @Async를 활용해 비동기 작업을 처리.
    • 작업 중에도 클라이언트 요청에 응답 가능.
    @RestController
    public class AsyncController {
    
        @Async
        @GetMapping("/async")
        public CompletableFuture<String> asyncExample() {
            return CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(3000); // 3초 대기
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "비동기 작업 완료";
            });
        }
    }

2. 콜백 헬 해결 실습

실습 목표

  • 자바에서 CompletableFutureWebClient를 활용해 비동기 작업을 구현하고 콜백 헬 문제를 해결.

실습 아이디어

  1. 콜백 헬 코드 작성

    • 외부 API를 호출한 후 데이터를 처리하는 코드 작성.
    public void callbackHellExample() {
        getUser(user -> {
            getPermissions(user, permissions -> {
                processData(permissions, result -> {
                    System.out.println("결과: " + result);
                });
            });
        });
    }
  2. 콜백 헬 해결 (CompletableFuture 사용)

    public void completableFutureExample() {
        CompletableFuture.supplyAsync(() -> getUser())
            .thenApply(user -> getPermissions(user))
            .thenApply(permissions -> processData(permissions))
            .thenAccept(result -> System.out.println("결과: " + result));
    }
  3. 콜백 헬 해결 (WebClient 사용)

    • WebClient를 활용해 API 호출을 비동기적으로 처리.
    @RestController
    public class WebClientExample {
    
        private final WebClient webClient;
    
        public WebClientExample(WebClient.Builder builder) {
            this.webClient = builder.baseUrl("https://api.example.com").build();
        }
    
        @GetMapping("/webclient")
        public Mono<String> getExample() {
            return webClient.get()
                    .uri("/data")
                    .retrieve()
                    .bodyToMono(String.class);
        }
    }

3. REST API 구현 실습

실습 목표

  • Spring Boot에서 REST API를 구축하고, 비동기 로직을 적용.

실습 아이디어

  1. 할 일 관리 애플리케이션

    • REST API로 할 일(TODO)을 관리:
      • 할 일 추가, 조회, 수정, 삭제 구현.
    • 비동기적으로 데이터베이스 처리.
    @RestController
    @RequestMapping("/todos")
    public class TodoController {
    
        @Autowired
        private TodoService todoService;
    
        @PostMapping
        public ResponseEntity<Todo> addTodo(@RequestBody Todo todo) {
            return ResponseEntity.ok(todoService.addTodo(todo));
        }
    
        @GetMapping
        public ResponseEntity<List<Todo>> getTodos() {
            return ResponseEntity.ok(todoService.getTodos());
        }
    }
  2. 외부 API 호출 및 데이터 처리

    • OpenWeatherMap API를 호출하여 날씨 정보를 가져오고, 사용자에게 전달.
    @RestController
    public class WeatherController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("/weather")
        public ResponseEntity<String> getWeather(@RequestParam String city) {
            String url = "https://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=your_api_key";
            String response = restTemplate.getForObject(url, String.class);
            return ResponseEntity.ok(response);
        }
    }

4. 데이터베이스 통합 및 비동기 처리

실습 목표

  • Spring Data JPA와 비동기 작업을 결합하여 데이터베이스 통신을 구현.

실습 아이디어

  1. Spring Data JPA와 @Async 활용

    • @AsyncCompletableFuture를 사용해 비동기 데이터베이스 쿼리 실행.
    @Service
    public class UserService {
    
        @Autowired
        private UserRepository userRepository;
    
        @Async
        public CompletableFuture<List<User>> getUsersAsync() {
            return CompletableFuture.supplyAsync(() -> userRepository.findAll());
        }
    }
  2. CRUD 작업 구현

    • MySQL 또는 H2 데이터베이스를 활용해 CRUD 작업 구현.
    @Entity
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private String email;
    
        // getters and setters
    }
    
    @Repository
    public interface UserRepository extends JpaRepository<User, Long> {}

5. 종합 프로젝트

실습 목표

  • 실무와 유사한 프로젝트를 완성하며 전반적인 기술 스택을 활용.

실습 아이디어

  1. 북 관리 애플리케이션

    • 기능: 책 추가, 삭제, 수정, 조회.
    • 기술 스택:
      • Spring Boot (REST API)
      • Spring Data JPA (MySQL/H2)
      • @Async로 비동기 데이터 처리.
  2. 날씨 대시보드

    • 기능: 도시별 날씨 정보 검색 및 저장.
    • 기술 스택:
      • WebClient로 OpenWeatherMap API 호출.
      • Spring Scheduler를 활용해 매일 날씨 데이터 자동 갱신.

6. 학습 자료와 도구


위 실습을 진행하면 Spring 기반 백엔드 개발자로서 필요한 역량을 쌓을 수 있습니다. 각 주제에서 도움이 필요한 부분이 있으면 언제든 질문해주세요! 😊

0개의 댓글