Frudy
7k
2019-06-04 09:38:30 작성 2019-06-04 09:42:54 수정됨
10
1974

try-catch에 대해 두가지 고민이 있습니다.


제가 생각하는 try-catch문의 스타일이 두가지가있습니다.

둘다 장단점이있어서, 


(1) 어느게 좋은지 나쁜지의 문제라서 어느한쪽을 지양해야하는 문제인지,

(2) 개발자의 코딩스타일이니 어느걸 써도 상관없는 문제인지,

궁금합니다.


개인프로젝트 3개를 진행해보면서 두가지 다 써봤지만,

여전히 해결되지않네요...


주제 : try-catch문을 분리해야할까 아닐까... 에요.


		try
		{
			//InvalidStringException이 발생할 가능성이 있는 코드
			
		}catch(InvalidStringException e)
		{
			
		}
		
		try
		{
			//NulberFormatException이 발생할 가능성이 있는 코드
			
		}catch(NumberFormatException e)
		{
			
		}
		
		
		try
		{
			//InvalidStringException이 발생할 가능성이 있는 코드
			//NulberFormatException이 발생할 가능성이 있는 코드
			
		}catch(InvalidStringException e)
		{

			
		}catch(NumberFormatException e)
		{

		}


고민1. 

try문에는, 해당 예외가 발생할 가능성이 있는 코드 위주로 오는게 좋다는 생각이에요.


try문에 코드가 200줄이 있는대,

그중 딱 한줄만 NumberFormatException이 발생할 가능성이 있다면,

나머지 199줄까지 try문에 때려박아야 할까? 에요.


메소드는, 하나의 기능만 해야한다는 원칙에 의거하여

아무리 코드를 분리하더라도,

하나의 메소드 안에 RuntimeException까지 포함하면

발생할 수 있는 예외가짓수가 2개이상이 넘는경우가 많아요.


그런경우, catch문을 보면서,

try문의 어느라인에 어떤 예외가 발생할 수 있는지 빠르게 캐치할 수 있어야하는대..


상황 : AException이 try문의 어느라인에서 발생할 가능성이 있는지 찾아야함

try문안에...

(1) 전혀 예외가 발생하지않는 코드

(2) BException이 발생할 가능성이 있는 코드

(3) AException이 발생할 가능성이 있는 코드

C D E 이렇게 죄다 있다면.....?


또한 그게, 예외끼리 상속관계가 전혀 맞지않는 아예 다른 주제의 예외라면...?

어우야...


결론1 >> try문 하나에 다 넣는 코딩은 나중에 읽는데 시간이 오래걸립니다.


ㅡㅡㅡ


그렇다고해서 분리하는게 마냥 장점만 있지 않습니다.


	public static void buy(int code, int money)
	{
		try
		{
			
			//물건을 사기위한 굉장히 긴 코드
		
		
//		돈이 없음
		}catch(NoMoneyException e)
		{
			
			
//		해당 제품번호의 물건 없음
		}catch(ProductNotDefException e)
		{
			
		}
	}


여기서 주목해주셔야할건, try-catch문만 벗어나면 메소드가 종료된다는 부분입니다.

코드 실행흐름을 저것과 동일하게 하면서 try-catch문을 분리하려면,


	public static void buy(int code, int money)
	{
//		두개의 try문에서 사용해야하는 변수
		int var = 0;
		
		try
		{
			var = 100;
		
		}catch(NoMoneyException e)
		{
//			예외처리
			return;
		}
		
		
		try
		{
			var = 20;
			
		}catch(ProductNotDefException e)
		{
//			예외처리
			return;
		}
	}

이렇게 각 catch문에서 return을 해야합니다.

하나의 try문으로 이루어져있으면 어떤 예외가발생해도 메소드가 종료되지만,

두개의 try문으로 이루어져있다면 catch문에서 return을 해줘야 종료되기 때문입니다.


또한, try문이 두개이기 때문에, 각각 try문에서 모두 사용되는 변수를

try문 바깥에서 미리 선언하고 가야합니다.


변수를 미리 선언하고, return을 꼬박꼬박 걸어야 해서

코드가 더러워집니다.


결론2 >> try-catch문을 여러개로 분리하면, 코드가 더러워지는 경향이 있습니다.


저는 일단 코드가 좀 길어지는걸 감수하고,

try문의 어느라인에 어떤 예외가 발생하는지를 빠르게 파악하는게 좋아서


@RequestMapping(value = "/answerForm.do")
	public ModelAndView answerForm(HttpServletRequest request, HttpServletResponse response)
	{
		ModelAndView modelAndView = new ModelAndView();
		
		HttpSession session = request.getSession();
		UserDto userDto = (UserDto)session.getAttribute("userDto");
		int inquiryCode = 0;
		
		try
		{
			inquiryCode = Integer.parseInt(request.getParameter("inquiryCode"));
			
		}catch(NumberFormatException e)
		{
			httpErrorService.setError500Page(modelAndView, "errorMessage", e.getMessage());
			return modelAndView;
		}
		
		try
		{
			if(userDto == null)
			{
				userDto = rememberMeService.getRememberMeUserDto(request.getCookies());
				
				if(userDto == null)
				{
					modelAndView.setViewName("redirect:" + LoginController.REDIRECT_JSP_LOGIN_FORM_PATH + "?previousPage=" + DO_ANSWER_FORM + "?inquiryCode=" + inquiryCode);
					return modelAndView;
				}
			}
			
		}catch(CookieManipulatedException e)
		{
			httpErrorService.setError500Page(modelAndView);
			modelAndView.addObject("errorMessage", e.getMessage());
			return modelAndView;
		}

이렇게 분리했었습니다.

1
  • 댓글 10

  • 팡팡이와아이들
    448
    2019-06-04 09:48:20 작성 2019-06-04 09:49:49 수정됨

    try catch를 한번에 묶어서 읽어야할 함수에 리턴값을 주면안되나요

    리턴 00 정상 if else -99 numberEx발생 -88 nullEx발생 이런식으로요~

    그담에 리턴값으로 해결해주면될거같긴한데~

    메세지도 뿌려줘야한다면 클래스로 받으시면될거같기도해용~

    좀더 간결한 코드가있으면 저도 배우고싶네요!

  • 개발자학도
    2k
    2019-06-04 09:50:32

    한가지 궁금한점이 있는데요

    inquiryCode = Integer.parseInt(request.getParameter("inquiryCode"));


    저부분에서 try catch 쓰는것보다 getParameter 값을 삼항연산자라 던가 

    다른조건문으로 걸러버리는 방법은 어떠신가요 

  • 팡팡이와아이들
    448
    2019-06-04 09:52:03

    앗 그러네용~_~코드를 좀더봐야겠지만 애초에 조건으로 한번 거를수있으면 거르는게 좋을거같네요~!

  • 팡팡이와아이들
    448
    2019-06-04 10:00:24

    500페이지를 보여줄려면 조건절에 걸렸을때 new thorw exception으로 띄우면안될까요 필요한 타입을 매개변수로 들고가면 해결될거같긴한데..

    질문을 꼼꼼히 못읽어봐서 이해가 잘안되서 그런가ㅜㅜ..

  • 팡팡이와아이들
    448
    2019-06-04 10:02:11

    함수부분을 클래스로 빼시고 스트링이나 클래스로 리턴받으면될거같다는건데 다시생각해보니

    애초에 많은조건이아니라면 처음에 거르시고 걸리면 throw exception 발생시키는게 더 나은거같기도해용!!큰도움 못줘서 미안해요!!

  • 더미
    15k
    2019-06-04 10:24:24

    예외처리에 대한 많은 가이드가 있습니다.

    오키에 있는 하마님 글에 대해 링크해드립니다.

    https://okky.kr/article/362305

  • 두더지
    365
    2019-06-04 10:29:14

    제 생각은 익셉션발생이 예상되는 부분부분들에 try / catch 구문으로 각각 분리해서 사용을 하든

    전체블록을 잡고 catch (Exception1, Exception2) 형식으로 사용을 하든 이건 만드는 사람의 의도만

    분명하다면 굳이 어느 한쪽을 지향하고 지양할 필요는 없을 듯싶습니다.

    저의 경우는 후자에 익셉션을 전체 처리하는데 애초에 메소드하나에 결합도가 높아서 라인수가 많아지지 않는 한 딱히 찾기 힘든 부분도 없어서 문제없이 사용했던거 같아요.

    개발과정에 있어서 프린트스택트레이스면 웬만한건 다 추적되니 ㅎㅎ;

  • 하마
    6k
    2019-06-04 10:34:58 작성 2019-06-04 11:03:39 수정됨

    이게 좀 답이 없긴하죠. 저도 잘 모르겠습니다.
    따라서 IBM 의 블록체인 소스코드 하나 소개해드립니다. 자신만의 확신을 갖는데 도움이 되길~
    Go언어로 되어 있으며, 참고로 Go 언어는 예외 같은거 안키웁니다.

     "error 리턴 코드로 가장 가까운 곳에서 즉시 처리"
    
    func (s *GossipStateProviderImpl) handleStateRequest(msg proto.ReceivedMessage) {
    	if msg == nil {
    		return
    	}
    	request := msg.GetGossipMessage().GetStateRequest()
    
    	batchSize := request.EndSeqNum - request.StartSeqNum
    	if batchSize > defAntiEntropyBatchSize {
    		logger.Errorf("Requesting blocks batchSize size (%d) greater than configured allowed"+
    			" (%d) batching for anti-entropy. Ignoring request...", batchSize, defAntiEntropyBatchSize)
    		return
    	}
    
    	if request.StartSeqNum > request.EndSeqNum {
    		logger.Errorf("Invalid sequence interval [%d...%d], ignoring request...", request.StartSeqNum, request.EndSeqNum)
    		return
    	}
    
    	currentHeight, err := s.ledger.LedgerHeight()
    	if err != nil {
    		logger.Errorf("Cannot access to current ledger height, due to %+v", errors.WithStack(err))
    		return
    	}
    	if currentHeight < request.EndSeqNum {
    		logger.Warningf("Received state request to transfer blocks with sequence numbers higher  [%d...%d] "+
    			"than available in ledger (%d)", request.StartSeqNum, request.StartSeqNum, currentHeight)
    	}
    
    	endSeqNum := min(currentHeight, request.EndSeqNum)
    
    	response := &proto.RemoteStateResponse{Payloads: make([]*proto.Payload, 0)}
    	for seqNum := request.StartSeqNum; seqNum <= endSeqNum; seqNum++ {
    		logger.Debug("Reading block ", seqNum, " with private data from the coordinator service")
    		connInfo := msg.GetConnectionInfo()
    		peerAuthInfo := common.SignedData{
    			Data:      connInfo.Auth.SignedData,
    			Signature: connInfo.Auth.Signature,
    			Identity:  connInfo.Identity,
    		}
    		block, pvtData, err := s.ledger.GetPvtDataAndBlockByNum(seqNum, peerAuthInfo)
    
    		if err != nil {
    			logger.Errorf("cannot read block number %d from ledger, because %+v, skipping...", seqNum, err)
    			continue
    		}
    
    		if block == nil {
    			logger.Errorf("Wasn't able to read block with sequence number %d from ledger, skipping....", seqNum)
    			continue
    		}
    
    		blockBytes, err := pb.Marshal(block)
    
    		if err != nil {
    			logger.Errorf("Could not marshal block: %+v", errors.WithStack(err))
    			continue
    		}
    
    		var pvtBytes [][]byte
    		if pvtData != nil {
    			// Marshal private data
    			pvtBytes, err = pvtData.Marshal()
    			if err != nil {
    				logger.Errorf("Failed to marshal private rwset for block %d due to %+v", seqNum, errors.WithStack(err))
    				continue
    			}
    		}
    
    		// Appending result to the response
    		response.Payloads = append(response.Payloads, &proto.Payload{
    			SeqNum:      seqNum,
    			Data:        blockBytes,
    			PrivateData: pvtBytes,
    		})
    	}
    	// Sending back response with missing blocks
    	msg.Respond(&proto.GossipMessage{
    		// Copy nonce field from the request, so it will be possible to match response
    		Nonce:   msg.GetGossipMessage().Nonce,
    		Tag:     proto.GossipMessage_CHAN_OR_ORG,
    		Channel: []byte(s.chainID),
    		Content: &proto.GossipMessage_StateResponse{StateResponse: response},
    	})
    }



  • ssssssu12
    211
    2019-06-04 12:13:12

    NumberFormatException은 if로 할 수 있겠네요~ (Apacha Common Lang 3.5)

    String inquiryCodeStr = request.getParameter("inquiryCode");
    if (!StringUtils.isNumeric(inquiryCodeStr)) {
        httpErrorService.setError500Page(modelAndView, "errorMessage", e.getMessage());
        return modelAndView;
    }
    inquiryCode = Integer.parseInt(inquiryCodeStr);


  • 개발자학도
    2k
    2019-06-04 12:38:56

    Frudy

    회의들어갔다와서 이제봤네요

    윗분들이 말씀하신 http통신이용해서 거르는 방법도있죠 ㅎㅎ

    저는 500에러전제인 상황이신지는 몰라서 그냥  조건문에서 변수가 숫자인지  확인후에

    틀리다면 return만 시키는 구조를 생각하고 말한거라서요 

  • 로그인을 하시면 댓글을 등록할 수 있습니다.