본문 바로가기

이론/JSP&Spring&등등 이론

[Spring] 싱글톤 레지스트리/ DI

싱글톤 레지스트리


"스프링은 기본적으로 싱글톤 오브젝트를 생성하는 싱글톤 레지스트리이다"

주의할 점

멀티스레드 환경에서 여러 스레드가 동시에 접속할 때 공유자원을 변경하게 되면 어떤 사용자가 변경하는 중에 다른 사용자가 사용하는 등의치명적인 문제가 발생한다.

따라서, 스프링으로 개발할 때 상태 정보를 내부에 갖고 있지 않는 무상태 방식을 취하는 것이 좋다.

내 코드로 예를 들면,

@Controller
public class ManagerController {
Logger logger=LoggerFactory.getLogger(ManagerController.class);

@Autowired
private FindUserService findUserService;

@Autowired
private S3Service s3Service;

@Autowired
private UserManagementService userManagementService;

@Autowired
private ReviseUserInfoServiceByManager reviseUserInfoService;

@PostMapping("/manager/reviseMemInfo.do")
public String reviseMemberInfoByPoset(@ModelAttribute("member") MemberDTO member, BindingResult bindingResult, HttpServletRequest req,Model model)
{
//주 pt횟수, 담당 트레이너 업데이트
try{
model.addAttribute("id",member.getId());
reviseUserInfoService.reviseMemberInfo(member);
}catch(Exception ex){
ex.printStackTrace();
return "redirect:/manager/reviseMemInfo.do?error&id="+member.getId();
}
model.addAttribute("management","active");
return "redirect:/manager/reviseMemInfo.do?success&id="+member.getId();

}
}


처음에는 관리자 기능을 명시하는 컨트롤러기 때문에 ManagerDTO 객체를 선언하였고, 첫 접속과 동시에 ManagerDAO를 통해 변수에 관리자를 저장하였다.

하지만, 만약 관리자가 여러명이고, 여러명이서 동시에 자신의 정보를 수정하거나 삭제하려고 한다면 문제가 발생한다. 클래스 내에 선언한 ManagerDTO 타입의 변수는 모든 스레드가 공유하고 있기 때문이다.

큰 깨달음을 얻고 변수를 삭제하고 ManagerDTO 객체를 사용해야하는 함수의 파라미터로 @AuthenticationPrincipal 을 통해 로그인 정보를 불러오는식으로 수정하였다. 파라미터로 넘겨받거나 지역 변수로 선언한 변수는 사용할 때마다 새로 생성하기 때문에 위와 같은 문제가 발생하지 않기 때문이다.


의존관계 주입(DI)


한 클래스가 다른 타입의 객체를 사용해야할 때 그 객체의 구현체를 직접 포함하는 것이 아닌 인터페이스를 포함하고 외부에서 인터페이스의 구현체를 주입함으로써 사용할 수 있도록 하는 것을 말한다. 이렇게 함으로써 구현 클래스와의 관계가 느슨해지고 그만큼 변화에 덜 민감하게 만들 수 있다. 실제로 런타임 시 주입되는 "의존 오브젝트"는 애플리케이션 컨텍스트와 같은 제 3자를 통해 주입된다.

@Bean
class DBConnecter{
   JDBCConfig jdbc; //jdbc 클래스에 담겨있는 설정 정보를 통해 DB 연결
   /*
   jdbc.getUrl(), jdbc.getUser(), jdbc.getPassword() 등을 통해 Connecter 반환
   */
}
interface JDBCConfig{
   public String getUrl();
   public String getUser();
   public String getPassword();
}
@Configuration
class LocalDBConfig implements JDBCConfig{
  @Override
   public String getUrl(){
       return "local url";
  }
  ..중략
}

이렇게 interface를 통해 의존을 설정한 후 런타임 시 LocalDBConfig가 연결되는 형태로 구현한다면, 후에 Local이 아닌 서버의 DB 설정을 사용할 경우, ServerDBConfig 라는 JDBCConfig 구현체를 생성한 뒤 @Configuration 을 붙이면 된다.

DBConnecter 클래스를 수정할 필요가 전혀 없으며, 하나의 클래스를 생성하기만 하면 된다.

만약 DBConnecter 클래스에서 LocalDBConfig를 직접 생성해서 사용했다면 변경 시 그 코드를 다시 수정해주었어야 하고, LocalDBConfig를 사용하는 클래스가 수십개 수백개라면 문제가 커진다.