동기(Synchronous Programming)
console.log("작업 시작");
alert("사용자 입력 대기 중...");
console.log("작업 완료"); // alert 창을 닫기 전까지 실행되지 않음
비동기 프로그래밍(Asynchronous Programming)
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);
});
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를 활용한 실습 아이디어입니다.
동기 처리 예제
@RestController
public class SyncController {
@GetMapping("/sync")
public String syncExample() throws InterruptedException {
Thread.sleep(3000); // 3초 대기
return "동기 작업 완료";
}
}
비동기 처리 예제
@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 "비동기 작업 완료";
});
}
}
CompletableFuture나 WebClient를 활용해 비동기 작업을 구현하고 콜백 헬 문제를 해결.콜백 헬 코드 작성
public void callbackHellExample() {
getUser(user -> {
getPermissions(user, permissions -> {
processData(permissions, result -> {
System.out.println("결과: " + result);
});
});
});
}
콜백 헬 해결 (CompletableFuture 사용)
public void completableFutureExample() {
CompletableFuture.supplyAsync(() -> getUser())
.thenApply(user -> getPermissions(user))
.thenApply(permissions -> processData(permissions))
.thenAccept(result -> System.out.println("결과: " + result));
}
콜백 헬 해결 (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);
}
}
할 일 관리 애플리케이션
@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());
}
}
외부 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);
}
}
Spring Data JPA와 @Async 활용
@Async와 CompletableFuture를 사용해 비동기 데이터베이스 쿼리 실행.@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Async
public CompletableFuture<List<User>> getUsersAsync() {
return CompletableFuture.supplyAsync(() -> userRepository.findAll());
}
}
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> {}
북 관리 애플리케이션
@Async로 비동기 데이터 처리.날씨 대시보드
위 실습을 진행하면 Spring 기반 백엔드 개발자로서 필요한 역량을 쌓을 수 있습니다. 각 주제에서 도움이 필요한 부분이 있으면 언제든 질문해주세요! 😊